From 643040d411b103ce7dcddc49c1278861100bf4b4 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 23 Jun 2026 22:18:27 +0200 Subject: [PATCH 1/3] fix: null check within union and blocked state --- src/schema/getSignatureSchema.ts | 77 +++++++++++++++++++++++++------- src/util/schema.util.ts | 21 +++++---- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/schema/getSignatureSchema.ts b/src/schema/getSignatureSchema.ts index b654281..1df0ccc 100644 --- a/src/schema/getSignatureSchema.ts +++ b/src/schema/getSignatureSchema.ts @@ -93,7 +93,7 @@ export const getSignatureSchema = ( ) // Identify parameter dependencies based on type parameters - const funktionDependencies = getParameterDependencies(funktion!) + const funktionDependencies = getParameterDependencies(funktion!, nodeParameterTypes) // Generate schema for each parameter return generateNodeSchemas( @@ -203,30 +203,57 @@ const extractFunctionParameterTypes = ( /** * Identifies parameter dependencies based on shared type parameters. * Determines which parameters depend on type parameters declared in other parameters. + * If an argument is explicitly provided (not null/undefined), it is not blocked. * - * @param node - The function declaration to analyze + * @param funktion - The function declaration to analyze + * @param nodeParameterTypes * @returns Array of ParameterDependency objects */ -const getParameterDependencies = (node: ts.FunctionDeclaration): ParameterDependency[] => { - // Extract all type parameter names from the function - const typeParamNames = node.typeParameters?.map((tp) => tp.name.getText()) || [] +const getParameterDependencies = ( + funktion: ts.FunctionDeclaration, + nodeParameterTypes: ts.Type[] | undefined +): ParameterDependency[] => { + const typeParamNames = funktion.typeParameters?.map((tp) => tp.name.getText()) || [] const usage: Record = {} // Track which parameters use each type parameter - node.parameters.forEach((p, i) => { - const typeText = p.type?.getText() || "" - typeParamNames.forEach((typeParam) => { - if (typeText.includes(typeParam)) { - if (!usage[typeParam]) { - usage[typeParam] = [] + funktion.parameters.forEach((p, i) => { + if (!p.type) return + + // Ein Set, um Duplikate pro Parameter zu vermeiden (falls 'A' mehrfach im selben Param-Typ vorkommt) + const foundInParameter = new Set() + + // Wir laufen rekursiv durch den Typ-Knoten des Parameters + p.type.forEachChild(function visitor(child) { + // Sucht nach expliziten Typ-Referenzen (z.B. die Typen in den <...> oder der Typ selbst) + if (ts.isTypeReferenceNode(child) && ts.isIdentifier(child.typeName)) { + const typeName = child.typeName.text + if (typeParamNames.includes(typeName)) { + foundInParameter.add(typeName) + } + } + // Falls der Typ selbst nur der Typparameter ist (z.B. p.type ist direkt ein TypeReferenceNode) + if (ts.isTypeReferenceNode(p.type!) && ts.isIdentifier(p.type.typeName)) { + const directTypeName = p.type.typeName.text + if (typeParamNames.includes(directTypeName)) { + foundInParameter.add(directTypeName) } - usage[typeParam].push(i) } + + child.forEachChild(visitor) + }) + + // Gefundene Abhängigkeiten für diesen Parameter registrieren + foundInParameter.forEach((typeParam) => { + if (!usage[typeParam]) { + usage[typeParam] = [] + } + usage[typeParam].push(i) }) }) - // Extract dependencies: type params used by multiple parameters - return Object.values(usage) + // Extract raw dependencies based on type definition + const rawDependencies = Object.values(usage) .filter((indices) => indices.length > 1) .map(([firstIndex, ...otherIndices]) => otherIndices.map((depIndex) => ({ @@ -235,8 +262,28 @@ const getParameterDependencies = (node: ts.FunctionDeclaration): ParameterDepend })), ) .flat() -} + // Wenn wir keine Typen vom Checker haben, bleiben wir beim Standard + if (!nodeParameterTypes) { + return rawDependencies + } + + // Filter heraus, was durch echte Werte (nicht null/undefined) bereits aufgelöst ist + return rawDependencies.filter((dep) => { + const resolvedType = nodeParameterTypes[dep.parameterIndex] + + // Falls aus irgendeinem Grund kein Typ ermittelt werden konnte -> blocked lassen + if (!resolvedType) return true + + // Prüfen, ob der Typ null oder undefined ist + const isNull = (resolvedType.flags & ts.TypeFlags.Null) !== 0 + const isUndefined = (resolvedType.flags & ts.TypeFlags.Undefined) !== 0 + + // Wenn es null oder undefined IST, bleibt es blocked (true) + // Wenn es ein echter Wert ist, fliegt die Dependency raus (false) + return isNull || isUndefined + }) +} /** * Generates node schemas for all parameters. * Creates schema objects for each parameter with their dependencies. diff --git a/src/util/schema.util.ts b/src/util/schema.util.ts index 9ca86fe..d2d5c8b 100644 --- a/src/util/schema.util.ts +++ b/src/util/schema.util.ts @@ -161,15 +161,15 @@ export const getSchema = ( ], } : {}; - // Strip undefined from unions (e.g. string | undefined → string). + // Strip undefined and null from unions (e.g. string | undefined | null → string). // Suggestions are collected above from the original type (preserving aliasSymbol literals), // the base schema is determined from the stripped type, then both are merged. if (parameterType.isUnion()) { - const nonUndefined = parameterType.types.filter( - (t) => (t.flags & ts.TypeFlags.Undefined) === 0 + const nonNullish = parameterType.types.filter( + (t) => (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) === 0 ) - if (nonUndefined.length === 1) { - const baseSchema = getSchema(checker, node, nonUndefined[0], functionDeclarations, functions, false) + if (nonNullish.length === 1) { + const baseSchema = getSchema(checker, node, nonNullish[0], functionDeclarations, functions, false) return {...baseSchema, ...combinedSuggestions} } } @@ -240,13 +240,13 @@ export const getSchema = ( (property.flags & ts.SymbolFlags.Optional) !== 0 || (propertyType.isUnion() && propertyType.types.some( - (t) => (t.flags & ts.TypeFlags.Undefined) !== 0 + (t) => (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) !== 0 )); - // Filter out undefined type from union types + // Filter out undefined and null types from union types const propertyTypes = propertyType.isUnion() ? propertyType.types.filter( - (t) => (t.flags & ts.TypeFlags.Undefined) === 0 + (t) => (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) === 0 ) : [propertyType]; @@ -358,7 +358,10 @@ function isStringOrNumberLiteral(type: ts.Type): boolean { */ function isPrimitiveLiteralUnion(type: ts.Type): boolean { if (!type.isUnion()) return false; - return type.types.every(isPrimitive); + const nonNullish = type.types.filter( + (t) => (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) === 0 + ); + return nonNullish.length > 0 && nonNullish.every(isPrimitive); } /** From 0982a70aa34f7c6b9c4cc112cad97c06c024c4fe Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 23 Jun 2026 22:18:40 +0200 Subject: [PATCH 2/3] fix: null check within validation --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 2942141..9742e69 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -134,7 +134,7 @@ export function generateFlowSourceCode( return `/* @pos ${id} ${index} */ ${refCode}`; } if (val.__typename === "LiteralValue") { - const jsonString = stringify(val?.value) + const jsonString = val?.value ? stringify(val?.value) : undefined return `/* @pos ${id} ${index} */ ${jsonString}`; } if (val.__typename === "SubFlowValue") { From 6e2ca2a24d329e86d2955c9e6de134154c8e4dff Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 23 Jun 2026 22:22:04 +0200 Subject: [PATCH 3/3] fix: better type matching --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 9742e69..7ce4955 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -134,7 +134,7 @@ export function generateFlowSourceCode( return `/* @pos ${id} ${index} */ ${refCode}`; } if (val.__typename === "LiteralValue") { - const jsonString = val?.value ? stringify(val?.value) : undefined + const jsonString = val?.value !== null && val?.value !== undefined ? stringify(val?.value) : undefined return `/* @pos ${id} ${index} */ ${jsonString}`; } if (val.__typename === "SubFlowValue") {