From b2f4f834c778deb3e8c18aa986aa1b2518d05973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 17 Aug 2024 19:59:23 +0200 Subject: [PATCH 1/4] Distribute `NoInfer` over unions and tuple element types --- src/compiler/checker.ts | 14 +- .../reference/narrowingNoInfer1.types | 44 +++---- .../reference/noInferRestSpread1.errors.txt | 33 +++++ .../reference/noInferRestSpread1.symbols | 80 ++++++++++++ .../reference/noInferRestSpread1.types | 123 ++++++++++++++++++ ...oInferUnionExcessPropertyCheck1.errors.txt | 4 +- .../noInferUnionExcessPropertyCheck1.types | 4 +- .../typeInference/noInferRestSpread1.ts | 27 ++++ 8 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 tests/baselines/reference/noInferRestSpread1.errors.txt create mode 100644 tests/baselines/reference/noInferRestSpread1.symbols create mode 100644 tests/baselines/reference/noInferRestSpread1.types create mode 100644 tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 91346429b1774..4d6391e91d1b5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16667,8 +16667,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.resolvedJSDocType; } - function getNoInferType(type: Type) { - return isNoInferTargetType(type) ? getOrCreateSubstitutionType(type, unknownType) : type; + function getNoInferType(type: Type): Type { + return mapType(type, t => { + if (isTupleType(t)) { + return createTupleType( + map(getElementTypes(t), getNoInferType), + t.target.elementFlags, + t.target.readonly, + t.target.labeledElementDeclarations, + ); + } + return isNoInferTargetType(t) ? getOrCreateSubstitutionType(t, unknownType) : t; + }); } function isNoInferTargetType(type: Type): boolean { diff --git a/tests/baselines/reference/narrowingNoInfer1.types b/tests/baselines/reference/narrowingNoInfer1.types index 690fcaa27be25..21f26749d6d05 100644 --- a/tests/baselines/reference/narrowingNoInfer1.types +++ b/tests/baselines/reference/narrowingNoInfer1.types @@ -22,8 +22,8 @@ type TaggedUnion = TaggedA | TaggedB; const m: { result: NoInfer }[] = []; >m : { result: NoInfer; }[] > : ^^^^^^^^^^ ^^^^^ ->result : NoInfer -> : ^^^^^^^^^^^^^^^^^^^^ +>result : NoInfer | NoInfer +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >[] : never[] > : ^^^^^^^ @@ -51,48 +51,48 @@ function map(items: readonly A[], f: (a: NoInfer) => B) { } const something = map(m, (_) => ->something : ({ result: TaggedA; } | null)[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->map(m, (_) => _.result._tag === "a" ? { ..._, result: _.result } : null,) : ({ result: TaggedA; } | null)[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>something : ({ result: NoInfer; } | null)[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>map(m, (_) => _.result._tag === "a" ? { ..._, result: _.result } : null,) : ({ result: NoInfer; } | null)[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >map : (items: readonly A[], f: (a: NoInfer) => B) => B[] > : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^ >m : { result: NoInfer; }[] > : ^^^^^^^^^^ ^^^^^ ->(_) => _.result._tag === "a" ? { ..._, result: _.result } : null : (_: NoInfer<{ result: NoInfer; }>) => { result: TaggedA; } | null -> : ^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(_) => _.result._tag === "a" ? { ..._, result: _.result } : null : (_: NoInfer<{ result: NoInfer; }>) => { result: NoInfer; } | null +> : ^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_ : NoInfer<{ result: NoInfer; }> > : ^^^^^^^^^^^^^^^^^^ ^^^^ _.result._tag === "a" ? { ..._, result: _.result } : null, ->_.result._tag === "a" ? { ..._, result: _.result } : null : { result: TaggedA; } | null -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>_.result._tag === "a" ? { ..._, result: _.result } : null : { result: NoInfer; } | null +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_.result._tag === "a" : boolean > : ^^^^^^^ >_.result._tag : "a" | "b" > : ^^^^^^^^^ ->_.result : TaggedUnion -> : ^^^^^^^^^^^ +>_.result : NoInfer | NoInfer +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : TaggedUnion -> : ^^^^^^^^^^^ +>result : NoInfer | NoInfer +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_tag : "a" | "b" > : ^^^^^^^^^ >"a" : "a" > : ^^^ ->{ ..._, result: _.result } : { result: TaggedA; } -> : ^^^^^^^^^^^^^^^^^^^^ +>{ ..._, result: _.result } : { result: NoInfer; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : TaggedA -> : ^^^^^^^ ->_.result : TaggedA -> : ^^^^^^^ +>result : NoInfer +> : ^^^^^^^^^^^^^^^^ +>_.result : NoInfer +> : ^^^^^^^^^^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : TaggedA -> : ^^^^^^^ +>result : NoInfer +> : ^^^^^^^^^^^^^^^^ ); diff --git a/tests/baselines/reference/noInferRestSpread1.errors.txt b/tests/baselines/reference/noInferRestSpread1.errors.txt new file mode 100644 index 0000000000000..16f2dcc18ae4c --- /dev/null +++ b/tests/baselines/reference/noInferRestSpread1.errors.txt @@ -0,0 +1,33 @@ +noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'. + Target signature provides too few arguments. Expected 2 or more, but got 1. + + +==== noInferRestSpread1.ts (1 errors) ==== + declare function call( + arg: (...args: NoInfer) => void, + ...args: A + ): A; + + const result1 = call((a: number) => {}, 1, 2); + const result2 = call((a: number, b: number) => {}, 1); // error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'. +!!! error TS2345: Target signature provides too few arguments. Expected 2 or more, but got 1. + + declare function fn1( + cb: (a: [number, ...NoInfer]) => void, + args: A, + ): A; + + declare const singleStr: [string]; + + const result3 = fn1((arg) => { + arg.length; + }, singleStr); + + declare const tupleUnion: [string] | [number, boolean]; + + const result4 = fn1((arg) => { + arg.length; + }, tupleUnion); + \ No newline at end of file diff --git a/tests/baselines/reference/noInferRestSpread1.symbols b/tests/baselines/reference/noInferRestSpread1.symbols new file mode 100644 index 0000000000000..74ec5f20e336e --- /dev/null +++ b/tests/baselines/reference/noInferRestSpread1.symbols @@ -0,0 +1,80 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts] //// + +=== noInferRestSpread1.ts === +declare function call( +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22)) + + arg: (...args: NoInfer) => void, +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 0, 52)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 1, 8)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22)) + + ...args: A +>args : Symbol(args, Decl(noInferRestSpread1.ts, 1, 37)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22)) + +): A; +>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22)) + +const result1 = call((a: number) => {}, 1, 2); +>result1 : Symbol(result1, Decl(noInferRestSpread1.ts, 5, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 5, 22)) + +const result2 = call((a: number, b: number) => {}, 1); // error +>result2 : Symbol(result2, Decl(noInferRestSpread1.ts, 6, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 6, 22)) +>b : Symbol(b, Decl(noInferRestSpread1.ts, 6, 32)) + +declare function fn1( +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) + + cb: (a: [number, ...NoInfer]) => void, +>cb : Symbol(cb, Decl(noInferRestSpread1.ts, 8, 42)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 9, 7)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) + + args: A, +>args : Symbol(args, Decl(noInferRestSpread1.ts, 9, 43)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) + +): A; +>A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) + +declare const singleStr: [string]; +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 13, 13)) + +const result3 = fn1((arg) => { +>result3 : Symbol(result3, Decl(noInferRestSpread1.ts, 15, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 15, 21)) + + arg.length; +>arg.length : Symbol(length) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 15, 21)) +>length : Symbol(length) + +}, singleStr); +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 13, 13)) + +declare const tupleUnion: [string] | [number, boolean]; +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 19, 13)) + +const result4 = fn1((arg) => { +>result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 21, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 21, 21)) + + arg.length; +>arg.length : Symbol(length) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 21, 21)) +>length : Symbol(length) + +}, tupleUnion); +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 19, 13)) + diff --git a/tests/baselines/reference/noInferRestSpread1.types b/tests/baselines/reference/noInferRestSpread1.types new file mode 100644 index 0000000000000..6590f9a5b3f94 --- /dev/null +++ b/tests/baselines/reference/noInferRestSpread1.types @@ -0,0 +1,123 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts] //// + +=== noInferRestSpread1.ts === +declare function call( +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ + + arg: (...args: NoInfer) => void, +>arg : (...args: NoInfer) => void +> : ^^^^ ^^ ^^^^^ +>args : NoInfer +> : ^^^^^^^^^^ + + ...args: A +>args : A +> : ^ + +): A; + +const result1 = call((a: number) => {}, 1, 2); +>result1 : [number, number] +> : ^^^^^^^^^^^^^^^^ +>call((a: number) => {}, 1, 2) : [number, number] +> : ^^^^^^^^^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(a: number) => {} : (a: number) => void +> : ^ ^^ ^^^^^^^^^ +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ +>2 : 2 +> : ^ + +const result2 = call((a: number, b: number) => {}, 1); // error +>result2 : [number] +> : ^^^^^^^^ +>call((a: number, b: number) => {}, 1) : [number] +> : ^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(a: number, b: number) => {} : (a: number, b: number) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : number +> : ^^^^^^ +>1 : 1 +> : ^ + +declare function fn1( +>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ + + cb: (a: [number, ...NoInfer]) => void, +>cb : (a: [number, ...NoInfer]) => void +> : ^ ^^ ^^^^^ +>a : [number, ...NoInfer] +> : ^^^^^^^^^^^^^^^^^^^^^^^ + + args: A, +>args : A +> : ^ + +): A; + +declare const singleStr: [string]; +>singleStr : [string] +> : ^^^^^^^^ + +const result3 = fn1((arg) => { +>result3 : [string] +> : ^^^^^^^^ +>fn1((arg) => { arg.length;}, singleStr) : [string] +> : ^^^^^^^^ +>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>(arg) => { arg.length;} : (arg: [number, string]) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arg : [number, string] +> : ^^^^^^^^^^^^^^^^ + + arg.length; +>arg.length : 2 +> : ^ +>arg : [number, string] +> : ^^^^^^^^^^^^^^^^ +>length : 2 +> : ^ + +}, singleStr); +>singleStr : [string] +> : ^^^^^^^^ + +declare const tupleUnion: [string] | [number, boolean]; +>tupleUnion : [string] | [number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +const result4 = fn1((arg) => { +>result4 : [string] | [number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn1((arg) => { arg.length;}, tupleUnion) : [string] | [number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>(arg) => { arg.length;} : (arg: [number, string] | [number, number, boolean]) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arg : [number, string] | [number, number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + arg.length; +>arg.length : 2 | 3 +> : ^^^^^ +>arg : [number, string] | [number, number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>length : 2 | 3 +> : ^^^^^ + +}, tupleUnion); +>tupleUnion : [string] | [number, boolean] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt index b5e6542911464..86b8aeb9bdfdd 100644 --- a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt +++ b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt @@ -1,6 +1,6 @@ noInferUnionExcessPropertyCheck1.ts(7,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | (() => NoInfer<{ x: string; }>)'. noInferUnionExcessPropertyCheck1.ts(15,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. -noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'. +noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. ==== noInferUnionExcessPropertyCheck1.ts (3 errors) ==== @@ -32,5 +32,5 @@ noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may onl test3({ x: "foo" }, { x: "bar" }); // no error test3({ x: "foo" }, { x: "bar", y: 42 }); // epc error ~ -!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'. +!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. \ No newline at end of file diff --git a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types index b1394d0e58c61..ef29042de0703 100644 --- a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types +++ b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types @@ -124,8 +124,8 @@ declare function test3( > : ^ b: NoInfer T)>, ->b : NoInfer T)> -> : ^^^^^^^^^^^^^^^^^^^ ^^ +>b : NoInfer | NoInfer<() => T> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ): void; diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts new file mode 100644 index 0000000000000..5e9b1ecabff83 --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts @@ -0,0 +1,27 @@ +// @strict: true +// @noEmit: true + +declare function call( + arg: (...args: NoInfer) => void, + ...args: A +): A; + +const result1 = call((a: number) => {}, 1, 2); +const result2 = call((a: number, b: number) => {}, 1); // error + +declare function fn1( + cb: (a: [number, ...NoInfer]) => void, + args: A, +): A; + +declare const singleStr: [string]; + +const result3 = fn1((arg) => { + arg.length; +}, singleStr); + +declare const tupleUnion: [string] | [number, boolean]; + +const result4 = fn1((arg) => { + arg.length; +}, tupleUnion); From 06c233a2e77342bdc41fe0e26a73d4e718a03637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 18 Aug 2024 08:35:34 +0200 Subject: [PATCH 2/4] Normalize `NoInfer`red tuples instead --- src/compiler/checker.ts | 56 +++++++++++-------- .../reference/narrowingNoInfer1.types | 44 +++++++-------- .../reference/noInferRestSpread1.errors.txt | 5 +- .../reference/noInferRestSpread1.symbols | 50 +++++++++-------- .../reference/noInferRestSpread1.types | 26 +++++++-- ...oInferUnionExcessPropertyCheck1.errors.txt | 4 +- .../noInferUnionExcessPropertyCheck1.types | 4 +- .../typeInference/noInferRestSpread1.ts | 5 +- 8 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4d6391e91d1b5..ad369bba9464f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13692,7 +13692,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (signatureHasRestParameter(sig)) { const restIndex = sig.parameters.length - 1; const restSymbol = sig.parameters[restIndex]; - const restType = getTypeOfSymbol(restSymbol); + const restType = normalizeNoInferTuple(getTypeOfSymbol(restSymbol)); if (isTupleType(restType)) { return [expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)]; } @@ -16032,7 +16032,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function tryGetRestTypeOfSignature(signature: Signature): Type | undefined { if (signatureHasRestParameter(signature)) { - const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); + const sigRestType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType; return restType && getIndexTypeOfType(restType, numberType); } @@ -16667,18 +16667,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.resolvedJSDocType; } - function getNoInferType(type: Type): Type { - return mapType(type, t => { - if (isTupleType(t)) { - return createTupleType( - map(getElementTypes(t), getNoInferType), - t.target.elementFlags, - t.target.readonly, - t.target.labeledElementDeclarations, - ); - } - return isNoInferTargetType(t) ? getOrCreateSubstitutionType(t, unknownType) : t; - }); + function getNoInferType(type: Type) { + return isNoInferTargetType(type) ? getOrCreateSubstitutionType(type, unknownType) : type; } function isNoInferTargetType(type: Type): boolean { @@ -17401,6 +17391,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTypeReference(target, elementTypes); } if (target.combinedFlags & ElementFlags.Variadic) { + elementTypes = sameMap(elementTypes, (t, i) => { + return target.elementFlags[i] & ElementFlags.Variadic ? normalizeNoInferTuple(t) : t; + }); // Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z] const unionIndex = findIndex(elementTypes, (t, i) => !!(target.elementFlags[i] & ElementFlags.Variadic && t.flags & (TypeFlags.Never | TypeFlags.Union))); if (unionIndex >= 0) { @@ -17487,6 +17480,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function normalizeNoInferTuple(type: Type): Type { + if (!isNoInferType(type)) { + return type; + } + return mapType((type as SubstitutionType).baseType, t => { + if (!isTupleType(t)) { + return getNoInferType(type); + } + return createTupleType( + map(getElementTypes(t), getNoInferType), + t.target.elementFlags, + t.target.readonly, + t.target.labeledElementDeclarations, + ); + }); + } + function sliceTupleType(type: TupleTypeReference, index: number, endSkipCount = 0) { const target = type.target; const endIndex = getTypeReferenceArity(type) - endSkipCount; @@ -31496,7 +31506,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const restIndex = signature.parameters.length - 1; return signatureHasRestParameter(signature) && argIndex >= restIndex ? - getIndexedAccessType(getTypeOfSymbol(signature.parameters[restIndex]), getNumberLiteralType(argIndex - restIndex), AccessFlags.Contextual) : + getIndexedAccessType(normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[restIndex])), getNumberLiteralType(argIndex - restIndex), AccessFlags.Contextual) : getTypeAtPosition(signature, argIndex); } @@ -37515,7 +37525,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return signature.parameters[pos].escapedName; } const restParameter = signature.parameters[paramCount] || unknownSymbol; - const restType = overrideRestType || getTypeOfSymbol(restParameter); + const restType = overrideRestType || normalizeNoInferTuple(getTypeOfSymbol(restParameter)); if (isTupleType(restType)) { const tupleType = (restType as TypeReference).target as TupleType; const index = pos - paramCount; @@ -37547,7 +37557,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } - const restType = getTypeOfSymbol(restParameter); + const restType = normalizeNoInferTuple(getTypeOfSymbol(restParameter)); if (isTupleType(restType)) { const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations; const index = pos - paramCount; @@ -37582,7 +37592,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return decl && isValidDeclarationForTupleLabel(decl) ? decl : undefined; } const restParameter = signature.parameters[paramCount] || unknownSymbol; - const restType = getTypeOfSymbol(restParameter); + const restType = normalizeNoInferTuple(getTypeOfSymbol(restParameter)); if (isTupleType(restType)) { const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations; const index = pos - paramCount; @@ -37604,7 +37614,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // We want to return the value undefined for an out of bounds parameter position, // so we need to check bounds here before calling getIndexedAccessType (which // otherwise would return the type 'undefined'). - const restType = getTypeOfSymbol(signature.parameters[paramCount]); + const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[paramCount])); const index = pos - paramCount; if (!isTupleType(restType) || restType.target.combinedFlags & ElementFlags.Variable || index < restType.target.fixedLength) { return getIndexedAccessType(restType, getNumberLiteralType(index)); @@ -37653,7 +37663,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getParameterCount(signature: Signature) { const length = signature.parameters.length; if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[length - 1]); + const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[length - 1])); if (isTupleType(restType)) { return length + restType.target.fixedLength - (restType.target.combinedFlags & ElementFlags.Variable ? 0 : 1); } @@ -37667,7 +37677,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (voidIsNonOptional || signature.resolvedMinArgumentCount === undefined) { let minArgumentCount: number | undefined; if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); + const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); if (isTupleType(restType)) { const firstOptionalIndex = findIndex(restType.target.elementFlags, f => !(f & ElementFlags.Required)); const requiredCount = firstOptionalIndex < 0 ? restType.target.fixedLength : firstOptionalIndex; @@ -37699,7 +37709,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function hasEffectiveRestParameter(signature: Signature) { if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); + const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); return !isTupleType(restType) || !!(restType.target.combinedFlags & ElementFlags.Variable); } return false; @@ -37707,7 +37717,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getEffectiveRestType(signature: Signature) { if (signatureHasRestParameter(signature)) { - const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); + const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); if (!isTupleType(restType)) { return isTypeAny(restType) ? anyArrayType : restType; } diff --git a/tests/baselines/reference/narrowingNoInfer1.types b/tests/baselines/reference/narrowingNoInfer1.types index 21f26749d6d05..690fcaa27be25 100644 --- a/tests/baselines/reference/narrowingNoInfer1.types +++ b/tests/baselines/reference/narrowingNoInfer1.types @@ -22,8 +22,8 @@ type TaggedUnion = TaggedA | TaggedB; const m: { result: NoInfer }[] = []; >m : { result: NoInfer; }[] > : ^^^^^^^^^^ ^^^^^ ->result : NoInfer | NoInfer -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>result : NoInfer +> : ^^^^^^^^^^^^^^^^^^^^ >[] : never[] > : ^^^^^^^ @@ -51,48 +51,48 @@ function map(items: readonly A[], f: (a: NoInfer) => B) { } const something = map(m, (_) => ->something : ({ result: NoInfer; } | null)[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->map(m, (_) => _.result._tag === "a" ? { ..._, result: _.result } : null,) : ({ result: NoInfer; } | null)[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>something : ({ result: TaggedA; } | null)[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>map(m, (_) => _.result._tag === "a" ? { ..._, result: _.result } : null,) : ({ result: TaggedA; } | null)[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >map : (items: readonly A[], f: (a: NoInfer) => B) => B[] > : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^ >m : { result: NoInfer; }[] > : ^^^^^^^^^^ ^^^^^ ->(_) => _.result._tag === "a" ? { ..._, result: _.result } : null : (_: NoInfer<{ result: NoInfer; }>) => { result: NoInfer; } | null -> : ^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(_) => _.result._tag === "a" ? { ..._, result: _.result } : null : (_: NoInfer<{ result: NoInfer; }>) => { result: TaggedA; } | null +> : ^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_ : NoInfer<{ result: NoInfer; }> > : ^^^^^^^^^^^^^^^^^^ ^^^^ _.result._tag === "a" ? { ..._, result: _.result } : null, ->_.result._tag === "a" ? { ..._, result: _.result } : null : { result: NoInfer; } | null -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>_.result._tag === "a" ? { ..._, result: _.result } : null : { result: TaggedA; } | null +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ >_.result._tag === "a" : boolean > : ^^^^^^^ >_.result._tag : "a" | "b" > : ^^^^^^^^^ ->_.result : NoInfer | NoInfer -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>_.result : TaggedUnion +> : ^^^^^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : NoInfer | NoInfer -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>result : TaggedUnion +> : ^^^^^^^^^^^ >_tag : "a" | "b" > : ^^^^^^^^^ >"a" : "a" > : ^^^ ->{ ..._, result: _.result } : { result: NoInfer; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ ..._, result: _.result } : { result: TaggedA; } +> : ^^^^^^^^^^^^^^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : NoInfer -> : ^^^^^^^^^^^^^^^^ ->_.result : NoInfer -> : ^^^^^^^^^^^^^^^^ +>result : TaggedA +> : ^^^^^^^ +>_.result : TaggedA +> : ^^^^^^^ >_ : { result: NoInfer; } > : ^^^^^^^^^^ ^^^ ->result : NoInfer -> : ^^^^^^^^^^^^^^^^ +>result : TaggedA +> : ^^^^^^^ ); diff --git a/tests/baselines/reference/noInferRestSpread1.errors.txt b/tests/baselines/reference/noInferRestSpread1.errors.txt index 16f2dcc18ae4c..bf5510daeac6d 100644 --- a/tests/baselines/reference/noInferRestSpread1.errors.txt +++ b/tests/baselines/reference/noInferRestSpread1.errors.txt @@ -13,6 +13,7 @@ noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: numb ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'. !!! error TS2345: Target signature provides too few arguments. Expected 2 or more, but got 1. + const result3 = call((a, b) => {}, 1, '');; // test contextual parameters declare function fn1( cb: (a: [number, ...NoInfer]) => void, @@ -21,13 +22,13 @@ noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: numb declare const singleStr: [string]; - const result3 = fn1((arg) => { + const result4 = fn1((arg) => { arg.length; }, singleStr); declare const tupleUnion: [string] | [number, boolean]; - const result4 = fn1((arg) => { + const result5 = fn1((arg) => { arg.length; }, tupleUnion); \ No newline at end of file diff --git a/tests/baselines/reference/noInferRestSpread1.symbols b/tests/baselines/reference/noInferRestSpread1.symbols index 74ec5f20e336e..462486f735f3f 100644 --- a/tests/baselines/reference/noInferRestSpread1.symbols +++ b/tests/baselines/reference/noInferRestSpread1.symbols @@ -29,52 +29,58 @@ const result2 = call((a: number, b: number) => {}, 1); // error >a : Symbol(a, Decl(noInferRestSpread1.ts, 6, 22)) >b : Symbol(b, Decl(noInferRestSpread1.ts, 6, 32)) +const result3 = call((a, b) => {}, 1, '');; // test contextual parameters +>result3 : Symbol(result3, Decl(noInferRestSpread1.ts, 7, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 7, 22)) +>b : Symbol(b, Decl(noInferRestSpread1.ts, 7, 24)) + declare function fn1( ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) cb: (a: [number, ...NoInfer]) => void, ->cb : Symbol(cb, Decl(noInferRestSpread1.ts, 8, 42)) ->a : Symbol(a, Decl(noInferRestSpread1.ts, 9, 7)) +>cb : Symbol(cb, Decl(noInferRestSpread1.ts, 9, 42)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 10, 7)) >NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) args: A, ->args : Symbol(args, Decl(noInferRestSpread1.ts, 9, 43)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 10, 43)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) ): A; ->A : Symbol(A, Decl(noInferRestSpread1.ts, 8, 21)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) declare const singleStr: [string]; ->singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 13, 13)) +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 14, 13)) -const result3 = fn1((arg) => { ->result3 : Symbol(result3, Decl(noInferRestSpread1.ts, 15, 5)) ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 15, 21)) +const result4 = fn1((arg) => { +>result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 16, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 16, 21)) arg.length; >arg.length : Symbol(length) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 15, 21)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 16, 21)) >length : Symbol(length) }, singleStr); ->singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 13, 13)) +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 14, 13)) declare const tupleUnion: [string] | [number, boolean]; ->tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 19, 13)) +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 20, 13)) -const result4 = fn1((arg) => { ->result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 21, 5)) ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 6, 54)) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 21, 21)) +const result5 = fn1((arg) => { +>result5 : Symbol(result5, Decl(noInferRestSpread1.ts, 22, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 22, 21)) arg.length; >arg.length : Symbol(length) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 21, 21)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 22, 21)) >length : Symbol(length) }, tupleUnion); ->tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 19, 13)) +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 20, 13)) diff --git a/tests/baselines/reference/noInferRestSpread1.types b/tests/baselines/reference/noInferRestSpread1.types index 6590f9a5b3f94..a12316bbd1173 100644 --- a/tests/baselines/reference/noInferRestSpread1.types +++ b/tests/baselines/reference/noInferRestSpread1.types @@ -49,6 +49,24 @@ const result2 = call((a: number, b: number) => {}, 1); // error >1 : 1 > : ^ +const result3 = call((a, b) => {}, 1, '');; // test contextual parameters +>result3 : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call((a, b) => {}, 1, '') : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(a, b) => {} : (a: number, b: string) => void +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : string +> : ^^^^^^ +>1 : 1 +> : ^ +>'' : "" +> : ^^ + declare function fn1( >fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A > : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ @@ -69,8 +87,8 @@ declare const singleStr: [string]; >singleStr : [string] > : ^^^^^^^^ -const result3 = fn1((arg) => { ->result3 : [string] +const result4 = fn1((arg) => { +>result4 : [string] > : ^^^^^^^^ >fn1((arg) => { arg.length;}, singleStr) : [string] > : ^^^^^^^^ @@ -97,8 +115,8 @@ declare const tupleUnion: [string] | [number, boolean]; >tupleUnion : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -const result4 = fn1((arg) => { ->result4 : [string] | [number, boolean] +const result5 = fn1((arg) => { +>result5 : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >fn1((arg) => { arg.length;}, tupleUnion) : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt index 86b8aeb9bdfdd..b5e6542911464 100644 --- a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt +++ b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.errors.txt @@ -1,6 +1,6 @@ noInferUnionExcessPropertyCheck1.ts(7,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | (() => NoInfer<{ x: string; }>)'. noInferUnionExcessPropertyCheck1.ts(15,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. -noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. +noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'. ==== noInferUnionExcessPropertyCheck1.ts (3 errors) ==== @@ -32,5 +32,5 @@ noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may onl test3({ x: "foo" }, { x: "bar" }); // no error test3({ x: "foo" }, { x: "bar", y: 42 }); // epc error ~ -!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'. +!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'. \ No newline at end of file diff --git a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types index ef29042de0703..b1394d0e58c61 100644 --- a/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types +++ b/tests/baselines/reference/noInferUnionExcessPropertyCheck1.types @@ -124,8 +124,8 @@ declare function test3( > : ^ b: NoInfer T)>, ->b : NoInfer | NoInfer<() => T> -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ +>b : NoInfer T)> +> : ^^^^^^^^^^^^^^^^^^^ ^^ ): void; diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts index 5e9b1ecabff83..0402f3e2d7e5b 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts @@ -8,6 +8,7 @@ declare function call( const result1 = call((a: number) => {}, 1, 2); const result2 = call((a: number, b: number) => {}, 1); // error +const result3 = call((a, b) => {}, 1, '');; // test contextual parameters declare function fn1( cb: (a: [number, ...NoInfer]) => void, @@ -16,12 +17,12 @@ declare function fn1( declare const singleStr: [string]; -const result3 = fn1((arg) => { +const result4 = fn1((arg) => { arg.length; }, singleStr); declare const tupleUnion: [string] | [number, boolean]; -const result4 = fn1((arg) => { +const result5 = fn1((arg) => { arg.length; }, tupleUnion); From 90572e4de697828481dd60a977205340e9591457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 19 Aug 2024 10:05:28 +0200 Subject: [PATCH 3/4] normalize early --- src/compiler/checker.ts | 45 ++++++++++++------- .../reference/noInferRestSpread1.errors.txt | 4 ++ .../reference/noInferRestSpread1.symbols | 11 +++++ .../reference/noInferRestSpread1.types | 20 +++++++++ .../typeInference/noInferRestSpread1.ts | 4 ++ 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad369bba9464f..3b16922672a3d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12192,8 +12192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal); } else if ( - isParameter(declaration) - || isPropertyDeclaration(declaration) + isPropertyDeclaration(declaration) || isPropertySignature(declaration) || isVariableDeclaration(declaration) || isBindingElement(declaration) @@ -12201,6 +12200,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ) { type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); } + else if (isParameter(declaration)) { + type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); + if (declaration.dotDotDotToken) { + type = normalizeNoInferSpread(type); + } + } // getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive. // Re-dispatch based on valueDeclaration.kind instead. else if (isEnumDeclaration(declaration)) { @@ -12424,7 +12429,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = getSymbolLinks(symbol); - return links.type || (links.type = instantiateType(getTypeOfSymbol(links.target!), links.mapper)); + if (!links.type) { + const declaration = links.target!.valueDeclaration; + let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper); + if (declaration && isParameter(declaration) && declaration.dotDotDotToken) { + type = normalizeNoInferSpread(type); + } + links.type = type; + } + return links.type; } function getWriteTypeOfInstantiatedSymbol(symbol: Symbol): Type { @@ -13692,7 +13705,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (signatureHasRestParameter(sig)) { const restIndex = sig.parameters.length - 1; const restSymbol = sig.parameters[restIndex]; - const restType = normalizeNoInferTuple(getTypeOfSymbol(restSymbol)); + const restType = getTypeOfSymbol(restSymbol); if (isTupleType(restType)) { return [expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)]; } @@ -16032,7 +16045,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function tryGetRestTypeOfSignature(signature: Signature): Type | undefined { if (signatureHasRestParameter(signature)) { - const sigRestType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); + const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType; return restType && getIndexTypeOfType(restType, numberType); } @@ -17392,7 +17405,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (target.combinedFlags & ElementFlags.Variadic) { elementTypes = sameMap(elementTypes, (t, i) => { - return target.elementFlags[i] & ElementFlags.Variadic ? normalizeNoInferTuple(t) : t; + return target.elementFlags[i] & ElementFlags.Variadic ? normalizeNoInferSpread(t) : t; }); // Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z] const unionIndex = findIndex(elementTypes, (t, i) => !!(target.elementFlags[i] & ElementFlags.Variadic && t.flags & (TypeFlags.Never | TypeFlags.Union))); @@ -17480,7 +17493,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function normalizeNoInferTuple(type: Type): Type { + function normalizeNoInferSpread(type: Type): Type { if (!isNoInferType(type)) { return type; } @@ -31506,7 +31519,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const restIndex = signature.parameters.length - 1; return signatureHasRestParameter(signature) && argIndex >= restIndex ? - getIndexedAccessType(normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[restIndex])), getNumberLiteralType(argIndex - restIndex), AccessFlags.Contextual) : + getIndexedAccessType(getTypeOfSymbol(signature.parameters[restIndex]), getNumberLiteralType(argIndex - restIndex), AccessFlags.Contextual) : getTypeAtPosition(signature, argIndex); } @@ -37525,7 +37538,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return signature.parameters[pos].escapedName; } const restParameter = signature.parameters[paramCount] || unknownSymbol; - const restType = overrideRestType || normalizeNoInferTuple(getTypeOfSymbol(restParameter)); + const restType = overrideRestType || getTypeOfSymbol(restParameter); if (isTupleType(restType)) { const tupleType = (restType as TypeReference).target as TupleType; const index = pos - paramCount; @@ -37557,7 +37570,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } - const restType = normalizeNoInferTuple(getTypeOfSymbol(restParameter)); + const restType = getTypeOfSymbol(restParameter); if (isTupleType(restType)) { const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations; const index = pos - paramCount; @@ -37592,7 +37605,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return decl && isValidDeclarationForTupleLabel(decl) ? decl : undefined; } const restParameter = signature.parameters[paramCount] || unknownSymbol; - const restType = normalizeNoInferTuple(getTypeOfSymbol(restParameter)); + const restType = getTypeOfSymbol(restParameter); if (isTupleType(restType)) { const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations; const index = pos - paramCount; @@ -37614,7 +37627,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // We want to return the value undefined for an out of bounds parameter position, // so we need to check bounds here before calling getIndexedAccessType (which // otherwise would return the type 'undefined'). - const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[paramCount])); + const restType = getTypeOfSymbol(signature.parameters[paramCount]); const index = pos - paramCount; if (!isTupleType(restType) || restType.target.combinedFlags & ElementFlags.Variable || index < restType.target.fixedLength) { return getIndexedAccessType(restType, getNumberLiteralType(index)); @@ -37663,7 +37676,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getParameterCount(signature: Signature) { const length = signature.parameters.length; if (signatureHasRestParameter(signature)) { - const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[length - 1])); + const restType = getTypeOfSymbol(signature.parameters[length - 1]); if (isTupleType(restType)) { return length + restType.target.fixedLength - (restType.target.combinedFlags & ElementFlags.Variable ? 0 : 1); } @@ -37677,7 +37690,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (voidIsNonOptional || signature.resolvedMinArgumentCount === undefined) { let minArgumentCount: number | undefined; if (signatureHasRestParameter(signature)) { - const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); + const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); if (isTupleType(restType)) { const firstOptionalIndex = findIndex(restType.target.elementFlags, f => !(f & ElementFlags.Required)); const requiredCount = firstOptionalIndex < 0 ? restType.target.fixedLength : firstOptionalIndex; @@ -37709,7 +37722,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function hasEffectiveRestParameter(signature: Signature) { if (signatureHasRestParameter(signature)) { - const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); + const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); return !isTupleType(restType) || !!(restType.target.combinedFlags & ElementFlags.Variable); } return false; @@ -37717,7 +37730,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getEffectiveRestType(signature: Signature) { if (signatureHasRestParameter(signature)) { - const restType = normalizeNoInferTuple(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1])); + const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); if (!isTupleType(restType)) { return isTypeAny(restType) ? anyArrayType : restType; } diff --git a/tests/baselines/reference/noInferRestSpread1.errors.txt b/tests/baselines/reference/noInferRestSpread1.errors.txt index bf5510daeac6d..1adf13727b243 100644 --- a/tests/baselines/reference/noInferRestSpread1.errors.txt +++ b/tests/baselines/reference/noInferRestSpread1.errors.txt @@ -31,4 +31,8 @@ noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: numb const result5 = fn1((arg) => { arg.length; }, tupleUnion); + + declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void; + + fn2((a, ...rest) => {}); \ No newline at end of file diff --git a/tests/baselines/reference/noInferRestSpread1.symbols b/tests/baselines/reference/noInferRestSpread1.symbols index 462486f735f3f..d7fb29ebd9ecf 100644 --- a/tests/baselines/reference/noInferRestSpread1.symbols +++ b/tests/baselines/reference/noInferRestSpread1.symbols @@ -84,3 +84,14 @@ const result5 = fn1((arg) => { }, tupleUnion); >tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 20, 13)) +declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void; +>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 24, 15)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 26, 21)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 26, 27)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +fn2((a, ...rest) => {}); +>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 24, 15)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 28, 5)) +>rest : Symbol(rest, Decl(noInferRestSpread1.ts, 28, 7)) + diff --git a/tests/baselines/reference/noInferRestSpread1.types b/tests/baselines/reference/noInferRestSpread1.types index a12316bbd1173..e261e6eb0ac36 100644 --- a/tests/baselines/reference/noInferRestSpread1.types +++ b/tests/baselines/reference/noInferRestSpread1.types @@ -139,3 +139,23 @@ const result5 = fn1((arg) => { >tupleUnion : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void; +>fn2 : (arg: (...args: NoInfer<[string, number]>) => void) => void +> : ^ ^^ ^^^^^ +>arg : (args_0: string, args_1: number) => void +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>args : [string, number] +> : ^^^^^^^^^^^^^^^^ + +fn2((a, ...rest) => {}); +>fn2((a, ...rest) => {}) : void +> : ^^^^ +>fn2 : (arg: (...args: NoInfer<[string, number]>) => void) => void +> : ^ ^^ ^^^^^ +>(a, ...rest) => {} : (a: string, rest_0: number) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ +>rest : [number] +> : ^^^^^^^^ + diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts index 0402f3e2d7e5b..638dbe3397fa4 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts @@ -26,3 +26,7 @@ declare const tupleUnion: [string] | [number, boolean]; const result5 = fn1((arg) => { arg.length; }, tupleUnion); + +declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void; + +fn2((a, ...rest) => {}); From 7be81d5ae9a5f2b98f102ea7222042af7a994d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 19 Aug 2024 10:28:07 +0200 Subject: [PATCH 4/4] add extra test cases for contextual parameters --- .../reference/noInferRestSpread1.errors.txt | 9 ++- .../reference/noInferRestSpread1.symbols | 76 +++++++++++-------- .../reference/noInferRestSpread1.types | 60 +++++++++++++-- .../typeInference/noInferRestSpread1.ts | 9 ++- 4 files changed, 113 insertions(+), 41 deletions(-) diff --git a/tests/baselines/reference/noInferRestSpread1.errors.txt b/tests/baselines/reference/noInferRestSpread1.errors.txt index 1adf13727b243..21f67ac0e0a60 100644 --- a/tests/baselines/reference/noInferRestSpread1.errors.txt +++ b/tests/baselines/reference/noInferRestSpread1.errors.txt @@ -13,7 +13,10 @@ noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: numb ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'. !!! error TS2345: Target signature provides too few arguments. Expected 2 or more, but got 1. - const result3 = call((a, b) => {}, 1, '');; // test contextual parameters + const result3 = call((a) => {}, 1, ''); // test contextual parameters + const result4 = call((a, b) => {}, 1, ''); // test contextual parameters + const result5 = call((...args) => {}, 1, ''); // test contextual parameters + const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters declare function fn1( cb: (a: [number, ...NoInfer]) => void, @@ -22,13 +25,13 @@ noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: numb declare const singleStr: [string]; - const result4 = fn1((arg) => { + const result7 = fn1((arg) => { arg.length; }, singleStr); declare const tupleUnion: [string] | [number, boolean]; - const result5 = fn1((arg) => { + const result8 = fn1((arg) => { arg.length; }, tupleUnion); diff --git a/tests/baselines/reference/noInferRestSpread1.symbols b/tests/baselines/reference/noInferRestSpread1.symbols index d7fb29ebd9ecf..18dc968f1abc5 100644 --- a/tests/baselines/reference/noInferRestSpread1.symbols +++ b/tests/baselines/reference/noInferRestSpread1.symbols @@ -29,69 +29,85 @@ const result2 = call((a: number, b: number) => {}, 1); // error >a : Symbol(a, Decl(noInferRestSpread1.ts, 6, 22)) >b : Symbol(b, Decl(noInferRestSpread1.ts, 6, 32)) -const result3 = call((a, b) => {}, 1, '');; // test contextual parameters +const result3 = call((a) => {}, 1, ''); // test contextual parameters >result3 : Symbol(result3, Decl(noInferRestSpread1.ts, 7, 5)) >call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) >a : Symbol(a, Decl(noInferRestSpread1.ts, 7, 22)) ->b : Symbol(b, Decl(noInferRestSpread1.ts, 7, 24)) + +const result4 = call((a, b) => {}, 1, ''); // test contextual parameters +>result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 8, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 8, 22)) +>b : Symbol(b, Decl(noInferRestSpread1.ts, 8, 24)) + +const result5 = call((...args) => {}, 1, ''); // test contextual parameters +>result5 : Symbol(result5, Decl(noInferRestSpread1.ts, 9, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 9, 22)) + +const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters +>result6 : Symbol(result6, Decl(noInferRestSpread1.ts, 10, 5)) +>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 10, 22)) +>rest : Symbol(rest, Decl(noInferRestSpread1.ts, 10, 24)) declare function fn1( ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21)) cb: (a: [number, ...NoInfer]) => void, ->cb : Symbol(cb, Decl(noInferRestSpread1.ts, 9, 42)) ->a : Symbol(a, Decl(noInferRestSpread1.ts, 10, 7)) +>cb : Symbol(cb, Decl(noInferRestSpread1.ts, 12, 42)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 13, 7)) >NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21)) args: A, ->args : Symbol(args, Decl(noInferRestSpread1.ts, 10, 43)) ->A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 13, 43)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21)) ): A; ->A : Symbol(A, Decl(noInferRestSpread1.ts, 9, 21)) +>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21)) declare const singleStr: [string]; ->singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 14, 13)) +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 17, 13)) -const result4 = fn1((arg) => { ->result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 16, 5)) ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 16, 21)) +const result7 = fn1((arg) => { +>result7 : Symbol(result7, Decl(noInferRestSpread1.ts, 19, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 19, 21)) arg.length; >arg.length : Symbol(length) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 16, 21)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 19, 21)) >length : Symbol(length) }, singleStr); ->singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 14, 13)) +>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 17, 13)) declare const tupleUnion: [string] | [number, boolean]; ->tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 20, 13)) +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 23, 13)) -const result5 = fn1((arg) => { ->result5 : Symbol(result5, Decl(noInferRestSpread1.ts, 22, 5)) ->fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 7, 43)) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 22, 21)) +const result8 = fn1((arg) => { +>result8 : Symbol(result8, Decl(noInferRestSpread1.ts, 25, 5)) +>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 25, 21)) arg.length; >arg.length : Symbol(length) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 22, 21)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 25, 21)) >length : Symbol(length) }, tupleUnion); ->tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 20, 13)) +>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 23, 13)) declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void; ->fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 24, 15)) ->arg : Symbol(arg, Decl(noInferRestSpread1.ts, 26, 21)) ->args : Symbol(args, Decl(noInferRestSpread1.ts, 26, 27)) +>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 27, 15)) +>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 29, 21)) +>args : Symbol(args, Decl(noInferRestSpread1.ts, 29, 27)) >NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) fn2((a, ...rest) => {}); ->fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 24, 15)) ->a : Symbol(a, Decl(noInferRestSpread1.ts, 28, 5)) ->rest : Symbol(rest, Decl(noInferRestSpread1.ts, 28, 7)) +>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 27, 15)) +>a : Symbol(a, Decl(noInferRestSpread1.ts, 31, 5)) +>rest : Symbol(rest, Decl(noInferRestSpread1.ts, 31, 7)) diff --git a/tests/baselines/reference/noInferRestSpread1.types b/tests/baselines/reference/noInferRestSpread1.types index e261e6eb0ac36..6ff3b0dbaf7ef 100644 --- a/tests/baselines/reference/noInferRestSpread1.types +++ b/tests/baselines/reference/noInferRestSpread1.types @@ -49,9 +49,25 @@ const result2 = call((a: number, b: number) => {}, 1); // error >1 : 1 > : ^ -const result3 = call((a, b) => {}, 1, '');; // test contextual parameters +const result3 = call((a) => {}, 1, ''); // test contextual parameters >result3 : [number, string] > : ^^^^^^^^^^^^^^^^ +>call((a) => {}, 1, '') : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(a) => {} : (a: number) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ +>'' : "" +> : ^^ + +const result4 = call((a, b) => {}, 1, ''); // test contextual parameters +>result4 : [number, string] +> : ^^^^^^^^^^^^^^^^ >call((a, b) => {}, 1, '') : [number, string] > : ^^^^^^^^^^^^^^^^ >call : (arg: (...args: NoInfer) => void, ...args: A) => A @@ -67,6 +83,40 @@ const result3 = call((a, b) => {}, 1, '');; // test contextual parameters >'' : "" > : ^^ +const result5 = call((...args) => {}, 1, ''); // test contextual parameters +>result5 : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call((...args) => {}, 1, '') : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(...args) => {} : (args_0: number, args_1: string) => void +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>args : [number, string] +> : ^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ +>'' : "" +> : ^^ + +const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters +>result6 : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call((a, ...rest) => {}, 1, '') : [number, string] +> : ^^^^^^^^^^^^^^^^ +>call : (arg: (...args: NoInfer) => void, ...args: A) => A +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>(a, ...rest) => {} : (a: number, rest_0: string) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>rest : [string] +> : ^^^^^^^^ +>1 : 1 +> : ^ +>'' : "" +> : ^^ + declare function fn1( >fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A > : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ @@ -87,8 +137,8 @@ declare const singleStr: [string]; >singleStr : [string] > : ^^^^^^^^ -const result4 = fn1((arg) => { ->result4 : [string] +const result7 = fn1((arg) => { +>result7 : [string] > : ^^^^^^^^ >fn1((arg) => { arg.length;}, singleStr) : [string] > : ^^^^^^^^ @@ -115,8 +165,8 @@ declare const tupleUnion: [string] | [number, boolean]; >tupleUnion : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -const result5 = fn1((arg) => { ->result5 : [string] | [number, boolean] +const result8 = fn1((arg) => { +>result8 : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >fn1((arg) => { arg.length;}, tupleUnion) : [string] | [number, boolean] > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts index 638dbe3397fa4..c86f2ec235a8d 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts @@ -8,7 +8,10 @@ declare function call( const result1 = call((a: number) => {}, 1, 2); const result2 = call((a: number, b: number) => {}, 1); // error -const result3 = call((a, b) => {}, 1, '');; // test contextual parameters +const result3 = call((a) => {}, 1, ''); // test contextual parameters +const result4 = call((a, b) => {}, 1, ''); // test contextual parameters +const result5 = call((...args) => {}, 1, ''); // test contextual parameters +const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters declare function fn1( cb: (a: [number, ...NoInfer]) => void, @@ -17,13 +20,13 @@ declare function fn1( declare const singleStr: [string]; -const result4 = fn1((arg) => { +const result7 = fn1((arg) => { arg.length; }, singleStr); declare const tupleUnion: [string] | [number, boolean]; -const result5 = fn1((arg) => { +const result8 = fn1((arg) => { arg.length; }, tupleUnion);