From bac5f90af192ac3b5b5bc56065a65985f9dafbd9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 19 Jul 2018 18:29:20 -0700 Subject: [PATCH 1/3] Properly handle all generic kinds of types in rest positions --- src/compiler/checker.ts | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 88983c9eff9..f1e282845d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10493,9 +10493,9 @@ namespace ts { } const sourceCount = getParameterCount(source); - const sourceRestTypeParameter = getRestTypeParameter(source); - const targetRestTypeParameter = sourceRestTypeParameter ? getRestTypeParameter(target) : undefined; - if (sourceRestTypeParameter && !(targetRestTypeParameter && sourceCount === targetCount)) { + const sourceGenericRestType = getGenericRestType(source); + const targetGenericRestType = sourceGenericRestType ? getGenericRestType(target) : undefined; + if (sourceGenericRestType && !(targetGenericRestType && sourceCount === targetCount)) { return Ternary.False; } @@ -10524,8 +10524,8 @@ namespace ts { const paramCount = Math.max(sourceCount, targetCount); const lastIndex = paramCount - 1; for (let i = 0; i < paramCount; i++) { - const sourceType = i === lastIndex && sourceRestTypeParameter || getTypeAtPosition(source, i); - const targetType = i === lastIndex && targetRestTypeParameter || getTypeAtPosition(target, i); + const sourceType = i === lastIndex && sourceGenericRestType || getTypeAtPosition(source, i); + const targetType = i === lastIndex && targetGenericRestType || getTypeAtPosition(target, i); // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, // they naturally relate only contra-variantly). However, if the source and target parameters both have @@ -12800,13 +12800,13 @@ namespace ts { sourceHasRest ? targetCount : targetHasRest ? sourceCount : Math.min(sourceCount, targetCount); - const targetRestTypeVariable = getRestTypeParameter(target); - const paramCount = targetRestTypeVariable ? Math.min(targetCount - 1, maxCount) : maxCount; + const targetGenericRestType = getGenericRestType(target); + const paramCount = targetGenericRestType ? Math.min(targetCount - 1, maxCount) : maxCount; for (let i = 0; i < paramCount; i++) { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } - if (targetRestTypeVariable) { - callback(getRestTypeAtPosition(source, paramCount), targetRestTypeVariable); + if (targetGenericRestType) { + callback(getRestTypeAtPosition(source, paramCount), targetGenericRestType); } } @@ -18376,8 +18376,8 @@ namespace ts { // We perform two passes over the arguments. In the first pass we infer from all arguments, but use // wildcards for all context sensitive function expressions. const effectiveArgCount = getEffectiveArgumentCount(node, args, signature); - const restTypeParameter = getRestTypeParameter(signature); - const argCount = restTypeParameter ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount; + const genericRestType = getGenericRestType(signature); + const argCount = genericRestType ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount; for (let i = 0; i < argCount; i++) { const arg = getEffectiveArgument(node, args, i); // If the effective argument is 'undefined', then it is an argument that is present but is synthetic. @@ -18396,9 +18396,9 @@ namespace ts { } } - if (restTypeParameter) { - const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, restTypeParameter, context); - inferTypes(context.inferences, spreadType, restTypeParameter); + if (genericRestType) { + const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, genericRestType, context); + inferTypes(context.inferences, spreadType, genericRestType); } // In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this @@ -19148,9 +19148,9 @@ namespace ts { } const isJavascript = isInJavaScriptFile(candidate.declaration); candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript); - // If the original signature has a rest type parameter, instantiation may produce a + // If the original signature has a generic rest type, instantiation may produce a // signature with different arity and we need to perform another arity check. - if (getRestTypeParameter(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) { + if (getGenericRestType(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) { candidateForArgumentArityError = candidate; break; } @@ -20080,9 +20080,9 @@ namespace ts { const paramCount = getParameterCount(source); const hasRest = hasEffectiveRestParameter(source); if (hasRest && pos === paramCount - 1) { - const restTypeVariable = getRestTypeParameter(source); - if (restTypeVariable) { - return restTypeVariable; + const genericRestType = getGenericRestType(source); + if (genericRestType) { + return genericRestType; } } const start = hasRest ? Math.min(pos, paramCount - 1) : pos; @@ -20132,11 +20132,11 @@ namespace ts { return signature.minArgumentCount; } - function getRestTypeParameter(signature: Signature) { + function getGenericRestType(signature: Signature) { if (signature.hasRestParameter) { const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - if (restType.flags & TypeFlags.TypeParameter) { - return restType; + if (restType.flags & TypeFlags.Instantiable) { + return restType; } } return undefined; From d5f3cee35d0c27ab0bc6b9887cf94952b48bc131 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 19 Jul 2018 18:29:30 -0700 Subject: [PATCH 2/3] Add regression test --- tests/cases/compiler/genericRestTypes.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/cases/compiler/genericRestTypes.ts diff --git a/tests/cases/compiler/genericRestTypes.ts b/tests/cases/compiler/genericRestTypes.ts new file mode 100644 index 00000000000..cdfbd54f70d --- /dev/null +++ b/tests/cases/compiler/genericRestTypes.ts @@ -0,0 +1,15 @@ +// @strict: true + +// Repro from #25793 + +// Gets the parameters of a function type as a tuple +type Parameters any> = T extends (...args: infer U) => any ? U : never; +// Removes the first element from a tuple +type Tail = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never; + +type MyFunctionType = (foo: number, bar: string) => boolean; + +type Explicit = (...args: Tail>) => ReturnType; // (bar: string) => boolean + +type Bind1 any> = (...args: Tail>) => ReturnType; +type Generic = Bind1; // (bar: string) => boolean From d0796ea5474b114eae35636a404ee75646e783db Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 19 Jul 2018 18:29:39 -0700 Subject: [PATCH 3/3] Accept new baselines --- tests/baselines/reference/genericRestTypes.js | 19 +++++++ .../reference/genericRestTypes.symbols | 55 +++++++++++++++++++ .../reference/genericRestTypes.types | 55 +++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 tests/baselines/reference/genericRestTypes.js create mode 100644 tests/baselines/reference/genericRestTypes.symbols create mode 100644 tests/baselines/reference/genericRestTypes.types diff --git a/tests/baselines/reference/genericRestTypes.js b/tests/baselines/reference/genericRestTypes.js new file mode 100644 index 00000000000..f932032af56 --- /dev/null +++ b/tests/baselines/reference/genericRestTypes.js @@ -0,0 +1,19 @@ +//// [genericRestTypes.ts] +// Repro from #25793 + +// Gets the parameters of a function type as a tuple +type Parameters any> = T extends (...args: infer U) => any ? U : never; +// Removes the first element from a tuple +type Tail = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never; + +type MyFunctionType = (foo: number, bar: string) => boolean; + +type Explicit = (...args: Tail>) => ReturnType; // (bar: string) => boolean + +type Bind1 any> = (...args: Tail>) => ReturnType; +type Generic = Bind1; // (bar: string) => boolean + + +//// [genericRestTypes.js] +"use strict"; +// Repro from #25793 diff --git a/tests/baselines/reference/genericRestTypes.symbols b/tests/baselines/reference/genericRestTypes.symbols new file mode 100644 index 00000000000..77bc400b5db --- /dev/null +++ b/tests/baselines/reference/genericRestTypes.symbols @@ -0,0 +1,55 @@ +=== tests/cases/compiler/genericRestTypes.ts === +// Repro from #25793 + +// Gets the parameters of a function type as a tuple +type Parameters any> = T extends (...args: infer U) => any ? U : never; +>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0)) +>T : Symbol(T, Decl(genericRestTypes.ts, 3, 16)) +>args : Symbol(args, Decl(genericRestTypes.ts, 3, 27)) +>T : Symbol(T, Decl(genericRestTypes.ts, 3, 16)) +>args : Symbol(args, Decl(genericRestTypes.ts, 3, 64)) +>U : Symbol(U, Decl(genericRestTypes.ts, 3, 78)) +>U : Symbol(U, Decl(genericRestTypes.ts, 3, 78)) + +// Removes the first element from a tuple +type Tail = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never; +>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101)) +>T : Symbol(T, Decl(genericRestTypes.ts, 5, 10)) +>args : Symbol(args, Decl(genericRestTypes.ts, 5, 31)) +>T : Symbol(T, Decl(genericRestTypes.ts, 5, 10)) +>head : Symbol(head, Decl(genericRestTypes.ts, 5, 61)) +>tail : Symbol(tail, Decl(genericRestTypes.ts, 5, 71)) +>U : Symbol(U, Decl(genericRestTypes.ts, 5, 86)) +>U : Symbol(U, Decl(genericRestTypes.ts, 5, 86)) + +type MyFunctionType = (foo: number, bar: string) => boolean; +>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110)) +>foo : Symbol(foo, Decl(genericRestTypes.ts, 7, 23)) +>bar : Symbol(bar, Decl(genericRestTypes.ts, 7, 35)) + +type Explicit = (...args: Tail>) => ReturnType; // (bar: string) => boolean +>Explicit : Symbol(Explicit, Decl(genericRestTypes.ts, 7, 60)) +>args : Symbol(args, Decl(genericRestTypes.ts, 9, 17)) +>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101)) +>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0)) +>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110)) + +type Bind1 any> = (...args: Tail>) => ReturnType; +>Bind1 : Symbol(Bind1, Decl(genericRestTypes.ts, 9, 90)) +>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11)) +>head : Symbol(head, Decl(genericRestTypes.ts, 11, 22)) +>tail : Symbol(tail, Decl(genericRestTypes.ts, 11, 32)) +>args : Symbol(args, Decl(genericRestTypes.ts, 11, 60)) +>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101)) +>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0)) +>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11)) + +type Generic = Bind1; // (bar: string) => boolean +>Generic : Symbol(Generic, Decl(genericRestTypes.ts, 11, 107)) +>Bind1 : Symbol(Bind1, Decl(genericRestTypes.ts, 9, 90)) +>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110)) + diff --git a/tests/baselines/reference/genericRestTypes.types b/tests/baselines/reference/genericRestTypes.types new file mode 100644 index 00000000000..6fc523bd7c9 --- /dev/null +++ b/tests/baselines/reference/genericRestTypes.types @@ -0,0 +1,55 @@ +=== tests/cases/compiler/genericRestTypes.ts === +// Repro from #25793 + +// Gets the parameters of a function type as a tuple +type Parameters any> = T extends (...args: infer U) => any ? U : never; +>Parameters : Parameters +>T : T +>args : any[] +>T : T +>args : U +>U : U +>U : U + +// Removes the first element from a tuple +type Tail = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never; +>Tail : Tail +>T : T +>args : T +>T : T +>head : any +>tail : U +>U : U +>U : U + +type MyFunctionType = (foo: number, bar: string) => boolean; +>MyFunctionType : MyFunctionType +>foo : number +>bar : string + +type Explicit = (...args: Tail>) => ReturnType; // (bar: string) => boolean +>Explicit : Explicit +>args : [string] +>Tail : Tail +>Parameters : Parameters +>MyFunctionType : MyFunctionType +>ReturnType : ReturnType +>MyFunctionType : MyFunctionType + +type Bind1 any> = (...args: Tail>) => ReturnType; +>Bind1 : Bind1 +>T : T +>head : any +>tail : any[] +>args : Tail> +>Tail : Tail +>Parameters : Parameters +>T : T +>ReturnType : ReturnType +>T : T + +type Generic = Bind1; // (bar: string) => boolean +>Generic : Bind1 +>Bind1 : Bind1 +>MyFunctionType : MyFunctionType +