From 81840373af091c815968d12b1b69d2865a515e0d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 12 Dec 2018 16:53:27 -0800 Subject: [PATCH 01/12] Add core of new union signature logic and test - needs intersection signature logic to fully work --- src/compiler/checker.ts | 111 ++++++++++++++++++ .../controlFlowArrayErrors.errors.txt | 6 +- ...onCallOnConstrainedTypeVariable.errors.txt | 22 ++-- .../reference/tsxUnionElementType1.errors.txt | 7 +- .../reference/tsxUnionElementType2.errors.txt | 7 +- .../reference/tsxUnionElementType4.errors.txt | 7 +- .../unionTypeCallSignatures.errors.txt | 21 ++-- .../unionTypeConstructSignatures.errors.txt | 21 ++-- .../reference/unionTypeMembers.errors.txt | 10 +- .../compiler/callsOnComplexSignatures.tsx | 106 +++++++++++++++++ 10 files changed, 276 insertions(+), 42 deletions(-) create mode 100644 tests/cases/compiler/callsOnComplexSignatures.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 356ddc2a28e1a..a849f0b5da9e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6729,7 +6729,17 @@ namespace ts { // type is the union of the constituent return types. function getUnionSignatures(signatureLists: ReadonlyArray>): Signature[] { let result: Signature[] | undefined; + let indexWithLengthOverOne: number | undefined; for (let i = 0; i < signatureLists.length; i++) { + if (signatureLists[i].length === 0) return emptyArray; + if (signatureLists[i].length > 1) { + if (indexWithLengthOverOne === undefined) { + indexWithLengthOverOne = i; + } + else { + indexWithLengthOverOne = -1; // signal there are multiple overload sets + } + } for (const signature of signatureLists[i]) { // Only process signatures with parameter lists that aren't already in the result list if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) { @@ -6753,9 +6763,110 @@ namespace ts { } } } + if (!length(result) && indexWithLengthOverOne !== -1) { + // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single + // signature that handles all over them. We only do this when there are overloads in only one constituent. + // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of + // signatures from the type, whose ordering would be non-obvious) + const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0]; + let results: Signature[] | undefined = masterList.slice(); + for (let i = 0; i < signatureLists.length; i++) { + const signatures = signatureLists[i]; + if (signatures === masterList) continue; + const signature = signatures[0]; + Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); + results = mergeAsUnionSignature(results, signature); + if (!results) { + break; + } + } + result = results; + } return result || emptyArray; } + function mergeAsUnionSignature(masterList: Signature[], addition: Signature): Signature[] | undefined { + if (addition.typeParameters && some(masterList, s => !!s.typeParameters)) { + return; // Can't currently unify type parameters + } + return map(masterList, sig => combineSignaturesOfUnionMembers(sig, addition)); + } + + function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { + if (!left && !right) { + return; + } + if (!left) { + return right; + } + if (!right) { + return left; + } + // A signature `this` type might be a read or a write position... It's very possible that it should be invariant + // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be + // permissive when calling, for now, we'll union the `this` types just like the overlapping-union-signature check does + const thisType = getUnionType([getTypeOfSymbol(left), getTypeOfSymbol(right)], UnionReduction.Subtype); + return createSymbolWithType(left, thisType); + } + + function combineUnionParameters(left: Signature, right: Signature) { + const longest = getParameterCount(left) >= getParameterCount(right) ? left : right; + const shorter = longest === left ? right : left; + const longestCount = getParameterCount(longest); + const eitherHasEffectiveRest = (hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right)); + const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); + let params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); + let i = 0; + for (; i < longestCount; i++) { + const longestParamType = tryGetTypeAtPosition(longest, i)!; + const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; + let unionParamType = getIntersectionType([longestParamType, shorterParamType]); + const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); + if (isRestParam) { + unionParamType = createArrayType(unionParamType); + } + const isOptional = i > getMinArgumentCount(longest); + // TODO: Synthesize symbol name from input signatures + const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), `arg${i}` as __String); + paramSymbol.type = unionParamType; + params[i] = paramSymbol; + } + if (needsExtraRestElement) { + const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); + restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, i)); + params[i] = restParamSymbol; + } + return params; + } + + function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { + const declaration = left.declaration; // TODO: Synthesize new declaration? Include field pointing to all backing declarations? + const params = combineUnionParameters(left, right); + const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); + const resolvedReturnType = left.resolvedReturnType && right.resolvedReturnType ? getUnionType([left.resolvedReturnType, right.resolvedReturnType]) : undefined; + const resolvedTypePredicate = left.resolvedTypePredicate && right.resolvedTypePredicate && left.resolvedTypePredicate.kind === right.resolvedTypePredicate.kind + ? left.resolvedTypePredicate.kind === TypePredicateKind.Identifier && left.resolvedTypePredicate.parameterIndex === (right.resolvedTypePredicate as IdentifierTypePredicate).parameterIndex + ? createIdentifierTypePredicate(unescapeLeadingUnderscores(params[left.resolvedTypePredicate.parameterIndex].escapedName), left.resolvedTypePredicate.parameterIndex, getUnionType([left.resolvedTypePredicate.type, right.resolvedTypePredicate.type])) + : left.resolvedTypePredicate.kind === TypePredicateKind.This + ? createThisTypePredicate(getUnionType([left.resolvedTypePredicate.type, right.resolvedTypePredicate.type])) + : undefined + : undefined; + const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); + const hasRestParam = left.hasRestParameter || right.hasRestParameter; + const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes; + return createSignature( + declaration, + left.typeParameters || right.typeParameters, + thisParam, + params, + resolvedReturnType, + resolvedTypePredicate, + minArgCount, + hasRestParam, + hasLiteralTypes + ); + } + function getUnionIndexInfo(types: ReadonlyArray, kind: IndexKind): IndexInfo | undefined { const indexTypes: Type[] = []; let isAnyReadonly = false; diff --git a/tests/baselines/reference/controlFlowArrayErrors.errors.txt b/tests/baselines/reference/controlFlowArrayErrors.errors.txt index 403cafacf5d23..59ec0b44f5c93 100644 --- a/tests/baselines/reference/controlFlowArrayErrors.errors.txt +++ b/tests/baselines/reference/controlFlowArrayErrors.errors.txt @@ -6,7 +6,7 @@ tests/cases/compiler/controlFlowArrayErrors.ts(19,9): error TS7034: Variable 'x' tests/cases/compiler/controlFlowArrayErrors.ts(22,9): error TS7005: Variable 'x' implicitly has an 'any[]' type. tests/cases/compiler/controlFlowArrayErrors.ts(29,12): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'. tests/cases/compiler/controlFlowArrayErrors.ts(34,12): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'. -tests/cases/compiler/controlFlowArrayErrors.ts(48,5): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((...items: (string | number)[]) => number) | ((...items: boolean[]) => number)' has no compatible call signatures. +tests/cases/compiler/controlFlowArrayErrors.ts(48,12): error TS2345: Argument of type '99' is not assignable to parameter of type 'never'. tests/cases/compiler/controlFlowArrayErrors.ts(56,12): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number'. tests/cases/compiler/controlFlowArrayErrors.ts(60,11): error TS7034: Variable 'x' implicitly has type 'any[]' in some locations where its type cannot be determined. tests/cases/compiler/controlFlowArrayErrors.ts(63,9): error TS7005: Variable 'x' implicitly has an 'any[]' type. @@ -77,8 +77,8 @@ tests/cases/compiler/controlFlowArrayErrors.ts(63,9): error TS7005: Variable 'x' } x; // boolean[] | (string | number)[] x.push(99); // Error - ~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((...items: (string | number)[]) => number) | ((...items: boolean[]) => number)' has no compatible call signatures. + ~~ +!!! error TS2345: Argument of type '99' is not assignable to parameter of type 'never'. } function f7() { diff --git a/tests/baselines/reference/functionCallOnConstrainedTypeVariable.errors.txt b/tests/baselines/reference/functionCallOnConstrainedTypeVariable.errors.txt index 1ff56c7612f7b..f7ea353e10f3c 100644 --- a/tests/baselines/reference/functionCallOnConstrainedTypeVariable.errors.txt +++ b/tests/baselines/reference/functionCallOnConstrainedTypeVariable.errors.txt @@ -1,7 +1,7 @@ -tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(11,3): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. -tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(15,3): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. -tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(18,3): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. -tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(19,3): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. +tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(11,7): error TS2345: Argument of type '"s"' is not assignable to parameter of type 'never'. +tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(15,7): error TS2345: Argument of type '"s"' is not assignable to parameter of type 'never'. +tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(18,5): error TS2345: Argument of type '""' is not assignable to parameter of type 'never'. +tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(19,3): error TS2554: Expected 1 arguments, but got 4. ==== tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts (4 errors) ==== @@ -16,20 +16,20 @@ tests/cases/compiler/functionCallOnConstrainedTypeVariable.ts(19,3): error TS234 function call0(p: A | B) { p.a("s"); // Error - ~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. + ~~~ +!!! error TS2345: Argument of type '"s"' is not assignable to parameter of type 'never'. } function callN(p: T) { p.a("s"); // Error - ~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. + ~~~ +!!! error TS2345: Argument of type '"s"' is not assignable to parameter of type 'never'. var a: T["a"] = p.a; a(""); // Error - ~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. + ~~ +!!! error TS2345: Argument of type '""' is not assignable to parameter of type 'never'. a("", "", "", ""); // Error ~~~~~~~~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((x: number) => string) | ((x: boolean) => string)' has no compatible call signatures. +!!! error TS2554: Expected 1 arguments, but got 4. } \ No newline at end of file diff --git a/tests/baselines/reference/tsxUnionElementType1.errors.txt b/tests/baselines/reference/tsxUnionElementType1.errors.txt index 9290e734d8388..e2f5bef633392 100644 --- a/tests/baselines/reference/tsxUnionElementType1.errors.txt +++ b/tests/baselines/reference/tsxUnionElementType1.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/jsx/file.tsx(12,2): error TS2604: JSX element type 'SFCComp' does not have any construct or call signatures. +tests/cases/conformance/jsx/file.tsx(12,10): error TS2322: Type 'true' is not assignable to type 'never'. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -14,5 +14,6 @@ tests/cases/conformance/jsx/file.tsx(12,2): error TS2604: JSX element type 'SFCC var SFCComp = SFC1 || SFC2; - ~~~~~~~ -!!! error TS2604: JSX element type 'SFCComp' does not have any construct or call signatures. \ No newline at end of file + ~ +!!! error TS2322: Type 'true' is not assignable to type 'never'. +!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:23: The expected type comes from property 'x' which is declared here on type 'IntrinsicAttributes & { x: number; } & { x: boolean; }' \ No newline at end of file diff --git a/tests/baselines/reference/tsxUnionElementType2.errors.txt b/tests/baselines/reference/tsxUnionElementType2.errors.txt index 091d828dfe373..87f1d7fc2dc58 100644 --- a/tests/baselines/reference/tsxUnionElementType2.errors.txt +++ b/tests/baselines/reference/tsxUnionElementType2.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/jsx/file.tsx(12,2): error TS2604: JSX element type 'SFCComp' does not have any construct or call signatures. +tests/cases/conformance/jsx/file.tsx(12,10): error TS2322: Type 'string' is not assignable to type 'never'. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -14,5 +14,6 @@ tests/cases/conformance/jsx/file.tsx(12,2): error TS2604: JSX element type 'SFCC var SFCComp = SFC1 || SFC2; - ~~~~~~~ -!!! error TS2604: JSX element type 'SFCComp' does not have any construct or call signatures. \ No newline at end of file + ~ +!!! error TS2322: Type 'string' is not assignable to type 'never'. +!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:23: The expected type comes from property 'x' which is declared here on type 'IntrinsicAttributes & { x: number; } & { x: boolean; }' \ No newline at end of file diff --git a/tests/baselines/reference/tsxUnionElementType4.errors.txt b/tests/baselines/reference/tsxUnionElementType4.errors.txt index 4ca07f265414f..736929d5356fc 100644 --- a/tests/baselines/reference/tsxUnionElementType4.errors.txt +++ b/tests/baselines/reference/tsxUnionElementType4.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/jsx/file.tsx(32,10): error TS2604: JSX element type 'RCComp' does not have any construct or call signatures. +tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'true' is not assignable to type 'ReactText'. tests/cases/conformance/jsx/file.tsx(33,10): error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. tests/cases/conformance/jsx/file.tsx(34,10): error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. @@ -38,8 +38,9 @@ tests/cases/conformance/jsx/file.tsx(34,10): error TS2322: Type '{ prop: true; } var PartRCComp = RC1 || RC4; // Error let a = ; - ~~~~~~ -!!! error TS2604: JSX element type 'RCComp' does not have any construct or call signatures. + ~ +!!! error TS2322: Type 'true' is not assignable to type 'ReactText'. +!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:36: The expected type comes from property 'x' which is declared here on type '(IntrinsicAttributes & IntrinsicClassAttributes & { x: number; } & { children?: ReactNode; }) | (IntrinsicAttributes & IntrinsicClassAttributes & { x: string; } & { children?: ReactNode; })' let b = ~~~~~~~~~~ !!! error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. diff --git a/tests/baselines/reference/unionTypeCallSignatures.errors.txt b/tests/baselines/reference/unionTypeCallSignatures.errors.txt index 5a67bde722c0e..c260d7ce51143 100644 --- a/tests/baselines/reference/unionTypeCallSignatures.errors.txt +++ b/tests/baselines/reference/unionTypeCallSignatures.errors.txt @@ -2,9 +2,11 @@ tests/cases/conformance/types/union/unionTypeCallSignatures.ts(9,43): error TS23 tests/cases/conformance/types/union/unionTypeCallSignatures.ts(10,29): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string'. tests/cases/conformance/types/union/unionTypeCallSignatures.ts(15,29): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string'. tests/cases/conformance/types/union/unionTypeCallSignatures.ts(16,1): error TS2554: Expected 1 arguments, but got 0. -tests/cases/conformance/types/union/unionTypeCallSignatures.ts(19,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. -tests/cases/conformance/types/union/unionTypeCallSignatures.ts(20,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. -tests/cases/conformance/types/union/unionTypeCallSignatures.ts(21,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. +tests/cases/conformance/types/union/unionTypeCallSignatures.ts(19,32): error TS2345: Argument of type '10' is not assignable to parameter of type 'number & string'. + Type '10' is not assignable to type 'string'. +tests/cases/conformance/types/union/unionTypeCallSignatures.ts(20,32): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number & string'. + Type '"hello"' is not assignable to type 'number'. +tests/cases/conformance/types/union/unionTypeCallSignatures.ts(21,1): error TS2554: Expected 1 arguments, but got 0. tests/cases/conformance/types/union/unionTypeCallSignatures.ts(24,1): error TS2554: Expected 1 arguments, but got 0. tests/cases/conformance/types/union/unionTypeCallSignatures.ts(26,36): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number'. tests/cases/conformance/types/union/unionTypeCallSignatures.ts(29,1): error TS2554: Expected 2 arguments, but got 0. @@ -56,14 +58,17 @@ tests/cases/conformance/types/union/unionTypeCallSignatures.ts(73,12): error TS2 var unionOfDifferentParameterTypes: { (a: number): number; } | { (a: string): Date; }; unionOfDifferentParameterTypes(10);// error - no call signatures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. + ~~ +!!! error TS2345: Argument of type '10' is not assignable to parameter of type 'number & string'. +!!! error TS2345: Type '10' is not assignable to type 'string'. unionOfDifferentParameterTypes("hello");// error - no call signatures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. + ~~~~~~~ +!!! error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number & string'. +!!! error TS2345: Type '"hello"' is not assignable to type 'number'. unionOfDifferentParameterTypes();// error - no call signatures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: number) => number) | ((a: string) => Date)' has no compatible call signatures. +!!! error TS2554: Expected 1 arguments, but got 0. +!!! related TS6210 tests/cases/conformance/types/union/unionTypeCallSignatures.ts:18:40: An argument for 'a' was not provided. var unionOfDifferentNumberOfSignatures: { (a: number): number; } | { (a: number): Date; (a: string): boolean; }; unionOfDifferentNumberOfSignatures(); // error - no call signatures diff --git a/tests/baselines/reference/unionTypeConstructSignatures.errors.txt b/tests/baselines/reference/unionTypeConstructSignatures.errors.txt index a5ad21ba5804d..92adc83a0d286 100644 --- a/tests/baselines/reference/unionTypeConstructSignatures.errors.txt +++ b/tests/baselines/reference/unionTypeConstructSignatures.errors.txt @@ -2,9 +2,11 @@ tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(9,47): error tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(10,33): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string'. tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(15,33): error TS2345: Argument of type 'true' is not assignable to parameter of type 'string'. tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(16,1): error TS2554: Expected 1 arguments, but got 0. -tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(19,1): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. -tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(20,1): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. -tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(21,1): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. +tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(19,36): error TS2345: Argument of type '10' is not assignable to parameter of type 'number & string'. + Type '10' is not assignable to type 'string'. +tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(20,36): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number & string'. + Type '"hello"' is not assignable to type 'number'. +tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(21,1): error TS2554: Expected 1 arguments, but got 0. tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(24,1): error TS2554: Expected 1 arguments, but got 0. tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(26,40): error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number'. tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(29,1): error TS2554: Expected 2 arguments, but got 0. @@ -55,14 +57,17 @@ tests/cases/conformance/types/union/unionTypeConstructSignatures.ts(70,12): erro var unionOfDifferentParameterTypes: { new (a: number): number; } | { new (a: string): Date; }; new unionOfDifferentParameterTypes(10);// error - no call signatures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. + ~~ +!!! error TS2345: Argument of type '10' is not assignable to parameter of type 'number & string'. +!!! error TS2345: Type '10' is not assignable to type 'string'. new unionOfDifferentParameterTypes("hello");// error - no call signatures - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. + ~~~~~~~ +!!! error TS2345: Argument of type '"hello"' is not assignable to parameter of type 'number & string'. +!!! error TS2345: Type '"hello"' is not assignable to type 'number'. new unionOfDifferentParameterTypes();// error - no call signatures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature. +!!! error TS2554: Expected 1 arguments, but got 0. +!!! related TS6210 tests/cases/conformance/types/union/unionTypeConstructSignatures.ts:18:44: An argument for 'a' was not provided. var unionOfDifferentNumberOfSignatures: { new (a: number): number; } | { new (a: number): Date; new (a: string): boolean; }; new unionOfDifferentNumberOfSignatures(); // error - no call signatures diff --git a/tests/baselines/reference/unionTypeMembers.errors.txt b/tests/baselines/reference/unionTypeMembers.errors.txt index 6d9f1321a74ca..2a2f93d1a071c 100644 --- a/tests/baselines/reference/unionTypeMembers.errors.txt +++ b/tests/baselines/reference/unionTypeMembers.errors.txt @@ -1,4 +1,6 @@ -tests/cases/conformance/types/union/unionTypeMembers.ts(44,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: string) => string) | ((a: number) => number)' has no compatible call signatures. +tests/cases/conformance/types/union/unionTypeMembers.ts(44,38): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string & number'. + Type 'string' is not assignable to type 'string & number'. + Type 'string' is not assignable to type 'number'. tests/cases/conformance/types/union/unionTypeMembers.ts(51,3): error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I1 | I2'. Property 'propertyOnlyInI1' does not exist on type 'I2'. tests/cases/conformance/types/union/unionTypeMembers.ts(52,3): error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1 | I2'. @@ -54,8 +56,10 @@ tests/cases/conformance/types/union/unionTypeMembers.ts(54,3): error TS2339: Pro strOrNum = x.commonMethodDifferentReturnType(str); // string | union x.commonMethodDifferentParameterType; // No error - property exists x.commonMethodDifferentParameterType(strOrNum); // error - no call signatures because the type of this property is ((a: string) => string) | (a: number) => number - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((a: string) => string) | ((a: number) => number)' has no compatible call signatures. + ~~~~~~~~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string & number'. +!!! error TS2345: Type 'string' is not assignable to type 'string & number'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. // and the call signatures arent identical num = x.commonMethodWithTypeParameter(num); num = x.commonMethodWithOwnTypeParameter(num); diff --git a/tests/cases/compiler/callsOnComplexSignatures.tsx b/tests/cases/compiler/callsOnComplexSignatures.tsx new file mode 100644 index 0000000000000..eb1ff9e753a6e --- /dev/null +++ b/tests/cases/compiler/callsOnComplexSignatures.tsx @@ -0,0 +1,106 @@ +// @jsx: react +// @esModuleInterop: true +// @strict: true +/// +import React from "react"; + +// Simple calls from real usecases +function test1() { + type stringType1 = "foo" | "bar"; + type stringType2 = "baz" | "bar"; + + interface Temp1 { + getValue(name: stringType1): number; + } + + interface Temp2 { + getValue(name: stringType2): string; + } + + function test(t: Temp1 | Temp2) { + const z = t.getValue("bar"); // Should be fine + } +} + +function test2() { + interface Messages { + readonly foo: (options: { [key: string]: any, b: number }) => string; + readonly bar: (options: { [key: string]: any, a: string }) => string; + } + + const messages: Messages = { + foo: (options) => "Foo", + bar: (options) => "Bar", + }; + + const test1 = (type: "foo" | "bar") => + messages[type]({ a: "A", b: 0 }); +} + +function test3(items: string[] | number[]) { + items.forEach(item => console.log(item)); +} + +function test4( + arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number), + arg2: ((a: {x: number}, b: object) => number) | ((a: object, b: {x: number}) => number), + arg3: ((a: {x: number}, ...objs: {y: number}[]) => number) | ((...objs: {x: number}[]) => number), + arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number), + arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number), + arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number), +) { + arg1(); + arg1({x: 0, y: 0}); + arg1({x: 0, y: 0}, {x: 1, y: 1}); + + arg2({x: 0}, {x: 0}); + + arg3({x: 0}); + arg3({x: 0}, {x: 0, y: 0}); + arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}); + + arg4(); + arg4({x: 0, y: 0}); + arg4({x: 0, y: 0}, {x: 0}); + + arg5(); + arg5({x: 0, y: 0}); + arg5({x: 0, y: 0}, {x: 0}); + + arg6(); + arg6({x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}); +} + +// JSX Tag names +function test5() { + // Pair of non-like intrinsics + function render(url?: string): React.ReactNode { + const Tag = url ? 'a' : 'button'; + return test; + } + + // Union of all intrinsics and components of `any` + function App(props: { component:React.ReactType }) { + const Comp: React.ReactType = props.component; + return (); + } + + // custom components with non-subset props + function render2() { + interface P1 { + p?: boolean; + c?: string; + } + interface P2 { + p?: boolean; + c?: any; + d?: any; + } + + var C: React.ComponentType | React.ComponentType = null as any; + + const a = ; + } +} From 6c2e1e18b86ac868c39e799f51a3a79103a2f8af Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 13 Dec 2018 14:22:34 -0800 Subject: [PATCH 02/12] Add inversion of variance for class props lookup from union sig returns --- src/compiler/checker.ts | 38 +- .../callsOnComplexSignatures.errors.txt | 110 +++++ .../reference/callsOnComplexSignatures.js | 169 +++++++ .../callsOnComplexSignatures.symbols | 334 ++++++++++++++ .../reference/callsOnComplexSignatures.types | 416 ++++++++++++++++++ .../reference/tsxUnionElementType3.errors.txt | 9 +- .../reference/tsxUnionElementType4.errors.txt | 8 +- .../tsxCompletionUnionElementType.ts | 2 +- .../tsxGoToDefinitionUnionElementType1.ts | 2 +- 9 files changed, 1068 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/callsOnComplexSignatures.errors.txt create mode 100644 tests/baselines/reference/callsOnComplexSignatures.js create mode 100644 tests/baselines/reference/callsOnComplexSignatures.symbols create mode 100644 tests/baselines/reference/callsOnComplexSignatures.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a849f0b5da9e7..5195f7ad25d3b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6840,31 +6840,25 @@ namespace ts { } function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { - const declaration = left.declaration; // TODO: Synthesize new declaration? Include field pointing to all backing declarations? + const declaration = left.declaration; const params = combineUnionParameters(left, right); const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); - const resolvedReturnType = left.resolvedReturnType && right.resolvedReturnType ? getUnionType([left.resolvedReturnType, right.resolvedReturnType]) : undefined; - const resolvedTypePredicate = left.resolvedTypePredicate && right.resolvedTypePredicate && left.resolvedTypePredicate.kind === right.resolvedTypePredicate.kind - ? left.resolvedTypePredicate.kind === TypePredicateKind.Identifier && left.resolvedTypePredicate.parameterIndex === (right.resolvedTypePredicate as IdentifierTypePredicate).parameterIndex - ? createIdentifierTypePredicate(unescapeLeadingUnderscores(params[left.resolvedTypePredicate.parameterIndex].escapedName), left.resolvedTypePredicate.parameterIndex, getUnionType([left.resolvedTypePredicate.type, right.resolvedTypePredicate.type])) - : left.resolvedTypePredicate.kind === TypePredicateKind.This - ? createThisTypePredicate(getUnionType([left.resolvedTypePredicate.type, right.resolvedTypePredicate.type])) - : undefined - : undefined; const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); const hasRestParam = left.hasRestParameter || right.hasRestParameter; const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes; - return createSignature( + const result = createSignature( declaration, left.typeParameters || right.typeParameters, thisParam, params, - resolvedReturnType, - resolvedTypePredicate, + /*resolvedReturnType*/ undefined, + /*resolvedTypePredicate*/ undefined, minArgCount, hasRestParam, hasLiteralTypes ); + result.unionSignatures = concatenate(left.unionSignatures || [left], [right]); + return result; } function getUnionIndexInfo(types: ReadonlyArray, kind: IndexKind): IndexInfo | undefined { @@ -17644,6 +17638,26 @@ namespace ts { } function getJsxPropsTypeForSignatureFromMember(sig: Signature, forcedLookupLocation: __String) { + if (sig.unionSignatures) { + // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input + // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature, + // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur + // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input. + // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane. + const results: Type[] = []; + for (const signature of sig.unionSignatures) { + const instance = getReturnTypeOfSignature(signature); + if (isTypeAny(instance)) { + return instance; + } + const propType = getTypeOfPropertyOfType(instance, forcedLookupLocation); + if (!propType) { + return; + } + results.push(propType); + } + return getIntersectionType(results); + } const instanceType = getReturnTypeOfSignature(sig); return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation); } diff --git a/tests/baselines/reference/callsOnComplexSignatures.errors.txt b/tests/baselines/reference/callsOnComplexSignatures.errors.txt new file mode 100644 index 0000000000000..f85a23e4ae79d --- /dev/null +++ b/tests/baselines/reference/callsOnComplexSignatures.errors.txt @@ -0,0 +1,110 @@ +tests/cases/compiler/callsOnComplexSignatures.tsx(38,19): error TS7006: Parameter 'item' implicitly has an 'any' type. + + +==== tests/cases/compiler/callsOnComplexSignatures.tsx (1 errors) ==== + /// + import React from "react"; + + // Simple calls from real usecases + function test1() { + type stringType1 = "foo" | "bar"; + type stringType2 = "baz" | "bar"; + + interface Temp1 { + getValue(name: stringType1): number; + } + + interface Temp2 { + getValue(name: stringType2): string; + } + + function test(t: Temp1 | Temp2) { + const z = t.getValue("bar"); // Should be fine + } + } + + function test2() { + interface Messages { + readonly foo: (options: { [key: string]: any, b: number }) => string; + readonly bar: (options: { [key: string]: any, a: string }) => string; + } + + const messages: Messages = { + foo: (options) => "Foo", + bar: (options) => "Bar", + }; + + const test1 = (type: "foo" | "bar") => + messages[type]({ a: "A", b: 0 }); + } + + function test3(items: string[] | number[]) { + items.forEach(item => console.log(item)); + ~~~~ +!!! error TS7006: Parameter 'item' implicitly has an 'any' type. + } + + function test4( + arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number), + arg2: ((a: {x: number}, b: object) => number) | ((a: object, b: {x: number}) => number), + arg3: ((a: {x: number}, ...objs: {y: number}[]) => number) | ((...objs: {x: number}[]) => number), + arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number), + arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number), + arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number), + ) { + arg1(); + arg1({x: 0, y: 0}); + arg1({x: 0, y: 0}, {x: 1, y: 1}); + + arg2({x: 0}, {x: 0}); + + arg3({x: 0}); + arg3({x: 0}, {x: 0, y: 0}); + arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}); + + arg4(); + arg4({x: 0, y: 0}); + arg4({x: 0, y: 0}, {x: 0}); + + arg5(); + arg5({x: 0, y: 0}); + arg5({x: 0, y: 0}, {x: 0}); + + arg6(); + arg6({x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}); + } + + // JSX Tag names + function test5() { + // Pair of non-like intrinsics + function render(url?: string): React.ReactNode { + const Tag = url ? 'a' : 'button'; + return test; + } + + // Union of all intrinsics and components of `any` + function App(props: { component:React.ReactType }) { + const Comp: React.ReactType = props.component; + return (); + } + + // custom components with non-subset props + function render2() { + interface P1 { + p?: boolean; + c?: string; + } + interface P2 { + p?: boolean; + c?: any; + d?: any; + } + + var C: React.ComponentType | React.ComponentType = null as any; + + const a = ; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/callsOnComplexSignatures.js b/tests/baselines/reference/callsOnComplexSignatures.js new file mode 100644 index 0000000000000..f631f674269cf --- /dev/null +++ b/tests/baselines/reference/callsOnComplexSignatures.js @@ -0,0 +1,169 @@ +//// [callsOnComplexSignatures.tsx] +/// +import React from "react"; + +// Simple calls from real usecases +function test1() { + type stringType1 = "foo" | "bar"; + type stringType2 = "baz" | "bar"; + + interface Temp1 { + getValue(name: stringType1): number; + } + + interface Temp2 { + getValue(name: stringType2): string; + } + + function test(t: Temp1 | Temp2) { + const z = t.getValue("bar"); // Should be fine + } +} + +function test2() { + interface Messages { + readonly foo: (options: { [key: string]: any, b: number }) => string; + readonly bar: (options: { [key: string]: any, a: string }) => string; + } + + const messages: Messages = { + foo: (options) => "Foo", + bar: (options) => "Bar", + }; + + const test1 = (type: "foo" | "bar") => + messages[type]({ a: "A", b: 0 }); +} + +function test3(items: string[] | number[]) { + items.forEach(item => console.log(item)); +} + +function test4( + arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number), + arg2: ((a: {x: number}, b: object) => number) | ((a: object, b: {x: number}) => number), + arg3: ((a: {x: number}, ...objs: {y: number}[]) => number) | ((...objs: {x: number}[]) => number), + arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number), + arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number), + arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number), +) { + arg1(); + arg1({x: 0, y: 0}); + arg1({x: 0, y: 0}, {x: 1, y: 1}); + + arg2({x: 0}, {x: 0}); + + arg3({x: 0}); + arg3({x: 0}, {x: 0, y: 0}); + arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}); + + arg4(); + arg4({x: 0, y: 0}); + arg4({x: 0, y: 0}, {x: 0}); + + arg5(); + arg5({x: 0, y: 0}); + arg5({x: 0, y: 0}, {x: 0}); + + arg6(); + arg6({x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}); + arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}); +} + +// JSX Tag names +function test5() { + // Pair of non-like intrinsics + function render(url?: string): React.ReactNode { + const Tag = url ? 'a' : 'button'; + return test; + } + + // Union of all intrinsics and components of `any` + function App(props: { component:React.ReactType }) { + const Comp: React.ReactType = props.component; + return (); + } + + // custom components with non-subset props + function render2() { + interface P1 { + p?: boolean; + c?: string; + } + interface P2 { + p?: boolean; + c?: any; + d?: any; + } + + var C: React.ComponentType | React.ComponentType = null as any; + + const a = ; + } +} + + +//// [callsOnComplexSignatures.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +/// +var react_1 = __importDefault(require("react")); +// Simple calls from real usecases +function test1() { + function test(t) { + var z = t.getValue("bar"); // Should be fine + } +} +function test2() { + var messages = { + foo: function (options) { return "Foo"; }, + bar: function (options) { return "Bar"; } + }; + var test1 = function (type) { + return messages[type]({ a: "A", b: 0 }); + }; +} +function test3(items) { + items.forEach(function (item) { return console.log(item); }); +} +function test4(arg1, arg2, arg3, arg4, arg5, arg6) { + arg1(); + arg1({ x: 0, y: 0 }); + arg1({ x: 0, y: 0 }, { x: 1, y: 1 }); + arg2({ x: 0 }, { x: 0 }); + arg3({ x: 0 }); + arg3({ x: 0 }, { x: 0, y: 0 }); + arg3({ x: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }); + arg4(); + arg4({ x: 0, y: 0 }); + arg4({ x: 0, y: 0 }, { x: 0 }); + arg5(); + arg5({ x: 0, y: 0 }); + arg5({ x: 0, y: 0 }, { x: 0 }); + arg6(); + arg6({ x: 0, y: 0 }); + arg6({ x: 0, y: 0 }, { x: 0, y: 0 }); + arg6({ x: 0, y: 0 }, { x: 0, y: 0 }, { y: 0 }); +} +// JSX Tag names +function test5() { + // Pair of non-like intrinsics + function render(url) { + var Tag = url ? 'a' : 'button'; + return react_1["default"].createElement(Tag, null, "test"); + } + // Union of all intrinsics and components of `any` + function App(props) { + var Comp = props.component; + return (react_1["default"].createElement(Comp, null)); + } + // custom components with non-subset props + function render2() { + var C = null; + var a = react_1["default"].createElement(C, { p: true }); + } +} diff --git a/tests/baselines/reference/callsOnComplexSignatures.symbols b/tests/baselines/reference/callsOnComplexSignatures.symbols new file mode 100644 index 0000000000000..dfbddce84ecf7 --- /dev/null +++ b/tests/baselines/reference/callsOnComplexSignatures.symbols @@ -0,0 +1,334 @@ +=== tests/cases/compiler/callsOnComplexSignatures.tsx === +/// +import React from "react"; +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) + +// Simple calls from real usecases +function test1() { +>test1 : Symbol(test1, Decl(callsOnComplexSignatures.tsx, 1, 26)) + + type stringType1 = "foo" | "bar"; +>stringType1 : Symbol(stringType1, Decl(callsOnComplexSignatures.tsx, 4, 18)) + + type stringType2 = "baz" | "bar"; +>stringType2 : Symbol(stringType2, Decl(callsOnComplexSignatures.tsx, 5, 37)) + + interface Temp1 { +>Temp1 : Symbol(Temp1, Decl(callsOnComplexSignatures.tsx, 6, 37)) + + getValue(name: stringType1): number; +>getValue : Symbol(Temp1.getValue, Decl(callsOnComplexSignatures.tsx, 8, 21)) +>name : Symbol(name, Decl(callsOnComplexSignatures.tsx, 9, 17)) +>stringType1 : Symbol(stringType1, Decl(callsOnComplexSignatures.tsx, 4, 18)) + } + + interface Temp2 { +>Temp2 : Symbol(Temp2, Decl(callsOnComplexSignatures.tsx, 10, 5)) + + getValue(name: stringType2): string; +>getValue : Symbol(Temp2.getValue, Decl(callsOnComplexSignatures.tsx, 12, 21)) +>name : Symbol(name, Decl(callsOnComplexSignatures.tsx, 13, 17)) +>stringType2 : Symbol(stringType2, Decl(callsOnComplexSignatures.tsx, 5, 37)) + } + + function test(t: Temp1 | Temp2) { +>test : Symbol(test, Decl(callsOnComplexSignatures.tsx, 14, 5)) +>t : Symbol(t, Decl(callsOnComplexSignatures.tsx, 16, 18)) +>Temp1 : Symbol(Temp1, Decl(callsOnComplexSignatures.tsx, 6, 37)) +>Temp2 : Symbol(Temp2, Decl(callsOnComplexSignatures.tsx, 10, 5)) + + const z = t.getValue("bar"); // Should be fine +>z : Symbol(z, Decl(callsOnComplexSignatures.tsx, 17, 13)) +>t.getValue : Symbol(getValue, Decl(callsOnComplexSignatures.tsx, 8, 21), Decl(callsOnComplexSignatures.tsx, 12, 21)) +>t : Symbol(t, Decl(callsOnComplexSignatures.tsx, 16, 18)) +>getValue : Symbol(getValue, Decl(callsOnComplexSignatures.tsx, 8, 21), Decl(callsOnComplexSignatures.tsx, 12, 21)) + } +} + +function test2() { +>test2 : Symbol(test2, Decl(callsOnComplexSignatures.tsx, 19, 1)) + + interface Messages { +>Messages : Symbol(Messages, Decl(callsOnComplexSignatures.tsx, 21, 18)) + + readonly foo: (options: { [key: string]: any, b: number }) => string; +>foo : Symbol(Messages.foo, Decl(callsOnComplexSignatures.tsx, 22, 24)) +>options : Symbol(options, Decl(callsOnComplexSignatures.tsx, 23, 23)) +>key : Symbol(key, Decl(callsOnComplexSignatures.tsx, 23, 35)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 23, 53)) + + readonly bar: (options: { [key: string]: any, a: string }) => string; +>bar : Symbol(Messages.bar, Decl(callsOnComplexSignatures.tsx, 23, 77)) +>options : Symbol(options, Decl(callsOnComplexSignatures.tsx, 24, 23)) +>key : Symbol(key, Decl(callsOnComplexSignatures.tsx, 24, 35)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 24, 53)) + } + + const messages: Messages = { +>messages : Symbol(messages, Decl(callsOnComplexSignatures.tsx, 27, 9)) +>Messages : Symbol(Messages, Decl(callsOnComplexSignatures.tsx, 21, 18)) + + foo: (options) => "Foo", +>foo : Symbol(foo, Decl(callsOnComplexSignatures.tsx, 27, 32)) +>options : Symbol(options, Decl(callsOnComplexSignatures.tsx, 28, 14)) + + bar: (options) => "Bar", +>bar : Symbol(bar, Decl(callsOnComplexSignatures.tsx, 28, 32)) +>options : Symbol(options, Decl(callsOnComplexSignatures.tsx, 29, 14)) + + }; + + const test1 = (type: "foo" | "bar") => +>test1 : Symbol(test1, Decl(callsOnComplexSignatures.tsx, 32, 9)) +>type : Symbol(type, Decl(callsOnComplexSignatures.tsx, 32, 19)) + + messages[type]({ a: "A", b: 0 }); +>messages : Symbol(messages, Decl(callsOnComplexSignatures.tsx, 27, 9)) +>type : Symbol(type, Decl(callsOnComplexSignatures.tsx, 32, 19)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 33, 24)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 33, 32)) +} + +function test3(items: string[] | number[]) { +>test3 : Symbol(test3, Decl(callsOnComplexSignatures.tsx, 34, 1)) +>items : Symbol(items, Decl(callsOnComplexSignatures.tsx, 36, 15)) + + items.forEach(item => console.log(item)); +>items.forEach : Symbol(forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(callsOnComplexSignatures.tsx, 36, 15)) +>forEach : Symbol(forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>item : Symbol(item, Decl(callsOnComplexSignatures.tsx, 37, 18)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>item : Symbol(item, Decl(callsOnComplexSignatures.tsx, 37, 18)) +} + +function test4( +>test4 : Symbol(test4, Decl(callsOnComplexSignatures.tsx, 38, 1)) + + arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number), +>arg1 : Symbol(arg1, Decl(callsOnComplexSignatures.tsx, 40, 15)) +>objs : Symbol(objs, Decl(callsOnComplexSignatures.tsx, 41, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 41, 22)) +>objs : Symbol(objs, Decl(callsOnComplexSignatures.tsx, 41, 51)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 41, 61)) + + arg2: ((a: {x: number}, b: object) => number) | ((a: object, b: {x: number}) => number), +>arg2 : Symbol(arg2, Decl(callsOnComplexSignatures.tsx, 41, 86)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 42, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 42, 16)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 42, 27)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 42, 54)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 42, 64)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 42, 69)) + + arg3: ((a: {x: number}, ...objs: {y: number}[]) => number) | ((...objs: {x: number}[]) => number), +>arg3 : Symbol(arg3, Decl(callsOnComplexSignatures.tsx, 42, 92)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 43, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 43, 16)) +>objs : Symbol(objs, Decl(callsOnComplexSignatures.tsx, 43, 27)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 43, 38)) +>objs : Symbol(objs, Decl(callsOnComplexSignatures.tsx, 43, 67)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 43, 77)) + + arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number), +>arg4 : Symbol(arg4, Decl(callsOnComplexSignatures.tsx, 43, 102)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 44, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 44, 17)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 44, 28)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 44, 34)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 44, 61)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 44, 66)) + + arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number), +>arg5 : Symbol(arg5, Decl(callsOnComplexSignatures.tsx, 44, 89)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 45, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 45, 17)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 45, 28)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 45, 36)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 45, 65)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 45, 70)) + + arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number), +>arg6 : Symbol(arg6, Decl(callsOnComplexSignatures.tsx, 45, 93)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 46, 12)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 46, 17)) +>b : Symbol(b, Decl(callsOnComplexSignatures.tsx, 46, 28)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 46, 34)) +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 46, 61)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 46, 68)) + +) { + arg1(); +>arg1 : Symbol(arg1, Decl(callsOnComplexSignatures.tsx, 40, 15)) + + arg1({x: 0, y: 0}); +>arg1 : Symbol(arg1, Decl(callsOnComplexSignatures.tsx, 40, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 49, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 49, 15)) + + arg1({x: 0, y: 0}, {x: 1, y: 1}); +>arg1 : Symbol(arg1, Decl(callsOnComplexSignatures.tsx, 40, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 50, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 50, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 50, 24)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 50, 29)) + + arg2({x: 0}, {x: 0}); +>arg2 : Symbol(arg2, Decl(callsOnComplexSignatures.tsx, 41, 86)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 52, 10)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 52, 18)) + + arg3({x: 0}); +>arg3 : Symbol(arg3, Decl(callsOnComplexSignatures.tsx, 42, 92)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 54, 10)) + + arg3({x: 0}, {x: 0, y: 0}); +>arg3 : Symbol(arg3, Decl(callsOnComplexSignatures.tsx, 42, 92)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 55, 10)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 55, 18)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 55, 23)) + + arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}); +>arg3 : Symbol(arg3, Decl(callsOnComplexSignatures.tsx, 42, 92)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 56, 10)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 56, 18)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 56, 23)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 56, 32)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 56, 37)) + + arg4(); +>arg4 : Symbol(arg4, Decl(callsOnComplexSignatures.tsx, 43, 102)) + + arg4({x: 0, y: 0}); +>arg4 : Symbol(arg4, Decl(callsOnComplexSignatures.tsx, 43, 102)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 59, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 59, 15)) + + arg4({x: 0, y: 0}, {x: 0}); +>arg4 : Symbol(arg4, Decl(callsOnComplexSignatures.tsx, 43, 102)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 60, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 60, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 60, 24)) + + arg5(); +>arg5 : Symbol(arg5, Decl(callsOnComplexSignatures.tsx, 44, 89)) + + arg5({x: 0, y: 0}); +>arg5 : Symbol(arg5, Decl(callsOnComplexSignatures.tsx, 44, 89)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 63, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 63, 15)) + + arg5({x: 0, y: 0}, {x: 0}); +>arg5 : Symbol(arg5, Decl(callsOnComplexSignatures.tsx, 44, 89)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 64, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 64, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 64, 24)) + + arg6(); +>arg6 : Symbol(arg6, Decl(callsOnComplexSignatures.tsx, 45, 93)) + + arg6({x: 0, y: 0}); +>arg6 : Symbol(arg6, Decl(callsOnComplexSignatures.tsx, 45, 93)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 67, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 67, 15)) + + arg6({x: 0, y: 0}, {x: 0, y: 0}); +>arg6 : Symbol(arg6, Decl(callsOnComplexSignatures.tsx, 45, 93)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 68, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 68, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 68, 24)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 68, 29)) + + arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}); +>arg6 : Symbol(arg6, Decl(callsOnComplexSignatures.tsx, 45, 93)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 69, 10)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 69, 15)) +>x : Symbol(x, Decl(callsOnComplexSignatures.tsx, 69, 24)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 69, 29)) +>y : Symbol(y, Decl(callsOnComplexSignatures.tsx, 69, 38)) +} + +// JSX Tag names +function test5() { +>test5 : Symbol(test5, Decl(callsOnComplexSignatures.tsx, 70, 1)) + + // Pair of non-like intrinsics + function render(url?: string): React.ReactNode { +>render : Symbol(render, Decl(callsOnComplexSignatures.tsx, 73, 18)) +>url : Symbol(url, Decl(callsOnComplexSignatures.tsx, 75, 20)) +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) +>ReactNode : Symbol(React.ReactNode, Decl(react16.d.ts, 216, 49)) + + const Tag = url ? 'a' : 'button'; +>Tag : Symbol(Tag, Decl(callsOnComplexSignatures.tsx, 76, 13)) +>url : Symbol(url, Decl(callsOnComplexSignatures.tsx, 75, 20)) + + return test; +>Tag : Symbol(Tag, Decl(callsOnComplexSignatures.tsx, 76, 13)) +>Tag : Symbol(Tag, Decl(callsOnComplexSignatures.tsx, 76, 13)) + } + + // Union of all intrinsics and components of `any` + function App(props: { component:React.ReactType }) { +>App : Symbol(App, Decl(callsOnComplexSignatures.tsx, 78, 5)) +>props : Symbol(props, Decl(callsOnComplexSignatures.tsx, 81, 17)) +>component : Symbol(component, Decl(callsOnComplexSignatures.tsx, 81, 25)) +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) +>ReactType : Symbol(React.ReactType, Decl(react16.d.ts, 112, 21)) + + const Comp: React.ReactType = props.component; +>Comp : Symbol(Comp, Decl(callsOnComplexSignatures.tsx, 82, 13)) +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) +>ReactType : Symbol(React.ReactType, Decl(react16.d.ts, 112, 21)) +>props.component : Symbol(component, Decl(callsOnComplexSignatures.tsx, 81, 25)) +>props : Symbol(props, Decl(callsOnComplexSignatures.tsx, 81, 17)) +>component : Symbol(component, Decl(callsOnComplexSignatures.tsx, 81, 25)) + + return (); +>Comp : Symbol(Comp, Decl(callsOnComplexSignatures.tsx, 82, 13)) + } + + // custom components with non-subset props + function render2() { +>render2 : Symbol(render2, Decl(callsOnComplexSignatures.tsx, 84, 5)) + + interface P1 { +>P1 : Symbol(P1, Decl(callsOnComplexSignatures.tsx, 87, 24)) + + p?: boolean; +>p : Symbol(P1.p, Decl(callsOnComplexSignatures.tsx, 88, 22)) + + c?: string; +>c : Symbol(P1.c, Decl(callsOnComplexSignatures.tsx, 89, 24)) + } + interface P2 { +>P2 : Symbol(P2, Decl(callsOnComplexSignatures.tsx, 91, 9)) + + p?: boolean; +>p : Symbol(P2.p, Decl(callsOnComplexSignatures.tsx, 92, 22)) + + c?: any; +>c : Symbol(P2.c, Decl(callsOnComplexSignatures.tsx, 93, 24)) + + d?: any; +>d : Symbol(P2.d, Decl(callsOnComplexSignatures.tsx, 94, 20)) + } + + var C: React.ComponentType | React.ComponentType = null as any; +>C : Symbol(C, Decl(callsOnComplexSignatures.tsx, 98, 11)) +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) +>ComponentType : Symbol(React.ComponentType, Decl(react16.d.ts, 117, 60)) +>P1 : Symbol(P1, Decl(callsOnComplexSignatures.tsx, 87, 24)) +>React : Symbol(React, Decl(callsOnComplexSignatures.tsx, 1, 6)) +>ComponentType : Symbol(React.ComponentType, Decl(react16.d.ts, 117, 60)) +>P2 : Symbol(P2, Decl(callsOnComplexSignatures.tsx, 91, 9)) + + const a = ; +>a : Symbol(a, Decl(callsOnComplexSignatures.tsx, 100, 13)) +>C : Symbol(C, Decl(callsOnComplexSignatures.tsx, 98, 11)) +>p : Symbol(p, Decl(callsOnComplexSignatures.tsx, 100, 20)) + } +} + diff --git a/tests/baselines/reference/callsOnComplexSignatures.types b/tests/baselines/reference/callsOnComplexSignatures.types new file mode 100644 index 0000000000000..4eb90ac43e345 --- /dev/null +++ b/tests/baselines/reference/callsOnComplexSignatures.types @@ -0,0 +1,416 @@ +=== tests/cases/compiler/callsOnComplexSignatures.tsx === +/// +import React from "react"; +>React : typeof React + +// Simple calls from real usecases +function test1() { +>test1 : () => void + + type stringType1 = "foo" | "bar"; +>stringType1 : "foo" | "bar" + + type stringType2 = "baz" | "bar"; +>stringType2 : "bar" | "baz" + + interface Temp1 { + getValue(name: stringType1): number; +>getValue : (name: "foo" | "bar") => number +>name : "foo" | "bar" + } + + interface Temp2 { + getValue(name: stringType2): string; +>getValue : (name: "bar" | "baz") => string +>name : "bar" | "baz" + } + + function test(t: Temp1 | Temp2) { +>test : (t: Temp1 | Temp2) => void +>t : Temp1 | Temp2 + + const z = t.getValue("bar"); // Should be fine +>z : React.ReactText +>t.getValue("bar") : React.ReactText +>t.getValue : ((name: "foo" | "bar") => number) | ((name: "bar" | "baz") => string) +>t : Temp1 | Temp2 +>getValue : ((name: "foo" | "bar") => number) | ((name: "bar" | "baz") => string) +>"bar" : "bar" + } +} + +function test2() { +>test2 : () => void + + interface Messages { + readonly foo: (options: { [key: string]: any, b: number }) => string; +>foo : (options: { [key: string]: any; b: number; }) => string +>options : { [key: string]: any; b: number; } +>key : string +>b : number + + readonly bar: (options: { [key: string]: any, a: string }) => string; +>bar : (options: { [key: string]: any; a: string; }) => string +>options : { [key: string]: any; a: string; } +>key : string +>a : string + } + + const messages: Messages = { +>messages : Messages +>{ foo: (options) => "Foo", bar: (options) => "Bar", } : { foo: (options: { [key: string]: any; b: number; }) => string; bar: (options: { [key: string]: any; a: string; }) => string; } + + foo: (options) => "Foo", +>foo : (options: { [key: string]: any; b: number; }) => string +>(options) => "Foo" : (options: { [key: string]: any; b: number; }) => string +>options : { [key: string]: any; b: number; } +>"Foo" : "Foo" + + bar: (options) => "Bar", +>bar : (options: { [key: string]: any; a: string; }) => string +>(options) => "Bar" : (options: { [key: string]: any; a: string; }) => string +>options : { [key: string]: any; a: string; } +>"Bar" : "Bar" + + }; + + const test1 = (type: "foo" | "bar") => +>test1 : (type: "foo" | "bar") => string +>(type: "foo" | "bar") => messages[type]({ a: "A", b: 0 }) : (type: "foo" | "bar") => string +>type : "foo" | "bar" + + messages[type]({ a: "A", b: 0 }); +>messages[type]({ a: "A", b: 0 }) : string +>messages[type] : ((options: { [key: string]: any; b: number; }) => string) | ((options: { [key: string]: any; a: string; }) => string) +>messages : Messages +>type : "foo" | "bar" +>{ a: "A", b: 0 } : { a: string; b: number; } +>a : string +>"A" : "A" +>b : number +>0 : 0 +} + +function test3(items: string[] | number[]) { +>test3 : (items: string[] | number[]) => void +>items : string[] | number[] + + items.forEach(item => console.log(item)); +>items.forEach(item => console.log(item)) : void +>items.forEach : ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void) | ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) +>items : string[] | number[] +>forEach : ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void) | ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) +>item => console.log(item) : (item: any) => void +>item : any +>console.log(item) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>item : any +} + +function test4( +>test4 : (arg1: ((...objs: { x: number; }[]) => number) | ((...objs: { y: number; }[]) => number), arg2: ((a: { x: number; }, b: object) => number) | ((a: object, b: { x: number; }) => number), arg3: ((a: { x: number; }, ...objs: { y: number; }[]) => number) | ((...objs: { x: number; }[]) => number), arg4: ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number), arg5: ((a?: { x: number; } | undefined, ...b: { x: number; }[]) => number) | ((a?: { y: number; } | undefined) => number), arg6: ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number)) => void + + arg1: ((...objs: {x: number}[]) => number) | ((...objs: {y: number}[]) => number), +>arg1 : ((...objs: { x: number; }[]) => number) | ((...objs: { y: number; }[]) => number) +>objs : { x: number; }[] +>x : number +>objs : { y: number; }[] +>y : number + + arg2: ((a: {x: number}, b: object) => number) | ((a: object, b: {x: number}) => number), +>arg2 : ((a: { x: number; }, b: object) => number) | ((a: object, b: { x: number; }) => number) +>a : { x: number; } +>x : number +>b : object +>a : object +>b : { x: number; } +>x : number + + arg3: ((a: {x: number}, ...objs: {y: number}[]) => number) | ((...objs: {x: number}[]) => number), +>arg3 : ((a: { x: number; }, ...objs: { y: number; }[]) => number) | ((...objs: { x: number; }[]) => number) +>a : { x: number; } +>x : number +>objs : { y: number; }[] +>y : number +>objs : { x: number; }[] +>x : number + + arg4: ((a?: {x: number}, b?: {x: number}) => number) | ((a?: {y: number}) => number), +>arg4 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number) +>a : { x: number; } | undefined +>x : number +>b : { x: number; } | undefined +>x : number +>a : { y: number; } | undefined +>y : number + + arg5: ((a?: {x: number}, ...b: {x: number}[]) => number) | ((a?: {y: number}) => number), +>arg5 : ((a?: { x: number; } | undefined, ...b: { x: number; }[]) => number) | ((a?: { y: number; } | undefined) => number) +>a : { x: number; } | undefined +>x : number +>b : { x: number; }[] +>x : number +>a : { y: number; } | undefined +>y : number + + arg6: ((a?: {x: number}, b?: {x: number}) => number) | ((...a: {y: number}[]) => number), +>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number) +>a : { x: number; } | undefined +>x : number +>b : { x: number; } | undefined +>x : number +>a : { y: number; }[] +>y : number + +) { + arg1(); +>arg1() : number +>arg1 : ((...objs: { x: number; }[]) => number) | ((...objs: { y: number; }[]) => number) + + arg1({x: 0, y: 0}); +>arg1({x: 0, y: 0}) : number +>arg1 : ((...objs: { x: number; }[]) => number) | ((...objs: { y: number; }[]) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg1({x: 0, y: 0}, {x: 1, y: 1}); +>arg1({x: 0, y: 0}, {x: 1, y: 1}) : number +>arg1 : ((...objs: { x: number; }[]) => number) | ((...objs: { y: number; }[]) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 1, y: 1} : { x: number; y: number; } +>x : number +>1 : 1 +>y : number +>1 : 1 + + arg2({x: 0}, {x: 0}); +>arg2({x: 0}, {x: 0}) : number +>arg2 : ((a: { x: number; }, b: object) => number) | ((a: object, b: { x: number; }) => number) +>{x: 0} : { x: number; } +>x : number +>0 : 0 +>{x: 0} : { x: number; } +>x : number +>0 : 0 + + arg3({x: 0}); +>arg3({x: 0}) : number +>arg3 : ((a: { x: number; }, ...objs: { y: number; }[]) => number) | ((...objs: { x: number; }[]) => number) +>{x: 0} : { x: number; } +>x : number +>0 : 0 + + arg3({x: 0}, {x: 0, y: 0}); +>arg3({x: 0}, {x: 0, y: 0}) : number +>arg3 : ((a: { x: number; }, ...objs: { y: number; }[]) => number) | ((...objs: { x: number; }[]) => number) +>{x: 0} : { x: number; } +>x : number +>0 : 0 +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}); +>arg3({x: 0}, {x: 0, y: 0}, {x: 0, y: 0}) : number +>arg3 : ((a: { x: number; }, ...objs: { y: number; }[]) => number) | ((...objs: { x: number; }[]) => number) +>{x: 0} : { x: number; } +>x : number +>0 : 0 +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg4(); +>arg4() : number +>arg4 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number) + + arg4({x: 0, y: 0}); +>arg4({x: 0, y: 0}) : number +>arg4 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg4({x: 0, y: 0}, {x: 0}); +>arg4({x: 0, y: 0}, {x: 0}) : number +>arg4 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((a?: { y: number; } | undefined) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 0} : { x: number; } +>x : number +>0 : 0 + + arg5(); +>arg5() : number +>arg5 : ((a?: { x: number; } | undefined, ...b: { x: number; }[]) => number) | ((a?: { y: number; } | undefined) => number) + + arg5({x: 0, y: 0}); +>arg5({x: 0, y: 0}) : number +>arg5 : ((a?: { x: number; } | undefined, ...b: { x: number; }[]) => number) | ((a?: { y: number; } | undefined) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg5({x: 0, y: 0}, {x: 0}); +>arg5({x: 0, y: 0}, {x: 0}) : number +>arg5 : ((a?: { x: number; } | undefined, ...b: { x: number; }[]) => number) | ((a?: { y: number; } | undefined) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 0} : { x: number; } +>x : number +>0 : 0 + + arg6(); +>arg6() : number +>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number) + + arg6({x: 0, y: 0}); +>arg6({x: 0, y: 0}) : number +>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg6({x: 0, y: 0}, {x: 0, y: 0}); +>arg6({x: 0, y: 0}, {x: 0, y: 0}) : number +>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 + + arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}); +>arg6({x: 0, y: 0}, {x: 0, y: 0}, {y: 0}) : number +>arg6 : ((a?: { x: number; } | undefined, b?: { x: number; } | undefined) => number) | ((...a: { y: number; }[]) => number) +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{x: 0, y: 0} : { x: number; y: number; } +>x : number +>0 : 0 +>y : number +>0 : 0 +>{y: 0} : { y: number; } +>y : number +>0 : 0 +} + +// JSX Tag names +function test5() { +>test5 : () => void + + // Pair of non-like intrinsics + function render(url?: string): React.ReactNode { +>render : (url?: string | undefined) => React.ReactNode +>url : string | undefined +>React : any + + const Tag = url ? 'a' : 'button'; +>Tag : "a" | "button" +>url ? 'a' : 'button' : "a" | "button" +>url : string | undefined +>'a' : "a" +>'button' : "button" + + return test; +>test : JSX.Element +>Tag : "a" | "button" +>Tag : "a" | "button" + } + + // Union of all intrinsics and components of `any` + function App(props: { component:React.ReactType }) { +>App : (props: { component: React.ReactType; }) => JSX.Element +>props : { component: React.ReactType; } +>component : React.ReactType +>React : any + + const Comp: React.ReactType = props.component; +>Comp : React.ReactType +>React : any +>props.component : React.ReactType +>props : { component: React.ReactType; } +>component : React.ReactType + + return (); +>() : JSX.Element +> : JSX.Element +>Comp : React.ReactType + } + + // custom components with non-subset props + function render2() { +>render2 : () => void + + interface P1 { + p?: boolean; +>p : boolean | undefined + + c?: string; +>c : string | undefined + } + interface P2 { + p?: boolean; +>p : boolean | undefined + + c?: any; +>c : any + + d?: any; +>d : any + } + + var C: React.ComponentType | React.ComponentType = null as any; +>C : React.ComponentClass | React.StatelessComponent | React.ComponentClass | React.StatelessComponent +>React : any +>React : any +>null as any : any +>null : null + + const a = ; +>a : JSX.Element +> : JSX.Element +>C : React.ComponentClass | React.StatelessComponent | React.ComponentClass | React.StatelessComponent +>p : true +>true : true + } +} + diff --git a/tests/baselines/reference/tsxUnionElementType3.errors.txt b/tests/baselines/reference/tsxUnionElementType3.errors.txt index bfdb61f406107..8f8ce3c28bd73 100644 --- a/tests/baselines/reference/tsxUnionElementType3.errors.txt +++ b/tests/baselines/reference/tsxUnionElementType3.errors.txt @@ -1,4 +1,5 @@ -tests/cases/conformance/jsx/file.tsx(32,10): error TS2604: JSX element type 'RCComp' does not have any construct or call signatures. +tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'string' is not assignable to type 'number & string'. + Type 'string' is not assignable to type 'number'. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -34,8 +35,10 @@ tests/cases/conformance/jsx/file.tsx(32,10): error TS2604: JSX element type 'RCC var RCComp = RC1 || RC2; // OK let a = ; - ~~~~~~ -!!! error TS2604: JSX element type 'RCComp' does not have any construct or call signatures. + ~ +!!! error TS2322: Type 'string' is not assignable to type 'number & string'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:36: The expected type comes from property 'x' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes & { x: number; } & { children?: ReactNode; } & { x: string; } & { children?: ReactNode; }' let a1 = ; let a2 = ; let b = diff --git a/tests/baselines/reference/tsxUnionElementType4.errors.txt b/tests/baselines/reference/tsxUnionElementType4.errors.txt index 736929d5356fc..4b059d093f8ff 100644 --- a/tests/baselines/reference/tsxUnionElementType4.errors.txt +++ b/tests/baselines/reference/tsxUnionElementType4.errors.txt @@ -1,4 +1,5 @@ -tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'true' is not assignable to type 'ReactText'. +tests/cases/conformance/jsx/file.tsx(32,17): error TS2322: Type 'true' is not assignable to type 'number & string'. + Type 'true' is not assignable to type 'number'. tests/cases/conformance/jsx/file.tsx(33,10): error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. tests/cases/conformance/jsx/file.tsx(34,10): error TS2322: Type '{ prop: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. @@ -39,8 +40,9 @@ tests/cases/conformance/jsx/file.tsx(34,10): error TS2322: Type '{ prop: true; } // Error let a = ; ~ -!!! error TS2322: Type 'true' is not assignable to type 'ReactText'. -!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:36: The expected type comes from property 'x' which is declared here on type '(IntrinsicAttributes & IntrinsicClassAttributes & { x: number; } & { children?: ReactNode; }) | (IntrinsicAttributes & IntrinsicClassAttributes & { x: string; } & { children?: ReactNode; })' +!!! error TS2322: Type 'true' is not assignable to type 'number & string'. +!!! error TS2322: Type 'true' is not assignable to type 'number'. +!!! related TS6500 tests/cases/conformance/jsx/file.tsx:3:36: The expected type comes from property 'x' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes & { x: number; } & { children?: ReactNode; } & { x: string; } & { children?: ReactNode; }' let b = ~~~~~~~~~~ !!! error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & { children?: ReactNode; }'. diff --git a/tests/cases/fourslash/tsxCompletionUnionElementType.ts b/tests/cases/fourslash/tsxCompletionUnionElementType.ts index 7d458f83493af..b554d3851798d 100644 --- a/tests/cases/fourslash/tsxCompletionUnionElementType.ts +++ b/tests/cases/fourslash/tsxCompletionUnionElementType.ts @@ -19,4 +19,4 @@ //// var SFCComp = SFC1 || SFC2; //// -verify.completions({ marker: "", exact: undefined }); +verify.completions({ marker: "", exact: ["x"] }); diff --git a/tests/cases/fourslash/tsxGoToDefinitionUnionElementType1.ts b/tests/cases/fourslash/tsxGoToDefinitionUnionElementType1.ts index 9e4cb4e1b1be9..957b6b811e0d8 100644 --- a/tests/cases/fourslash/tsxGoToDefinitionUnionElementType1.ts +++ b/tests/cases/fourslash/tsxGoToDefinitionUnionElementType1.ts @@ -22,5 +22,5 @@ //// <[|SFC/*one*/Comp|] x /> verify.goToDefinition({ - "one": ["def"], + "one": ["def", "pt1"], }); From 8ea94cd507ea627b344ba008f6126fcff409a497 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 13 Dec 2018 15:26:36 -0800 Subject: [PATCH 03/12] Fix lints --- src/compiler/checker.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5195f7ad25d3b..deba784374c51 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6765,13 +6765,12 @@ namespace ts { } if (!length(result) && indexWithLengthOverOne !== -1) { // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single - // signature that handles all over them. We only do this when there are overloads in only one constituent. + // signature that handles all over them. We only do this when there are overloads in only one constituent. // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of // signatures from the type, whose ordering would be non-obvious) const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0]; let results: Signature[] | undefined = masterList.slice(); - for (let i = 0; i < signatureLists.length; i++) { - const signatures = signatureLists[i]; + for (const signatures of signatureLists) { if (signatures === masterList) continue; const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); @@ -6815,7 +6814,7 @@ namespace ts { const longestCount = getParameterCount(longest); const eitherHasEffectiveRest = (hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right)); const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); - let params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); + const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); let i = 0; for (; i < longestCount; i++) { const longestParamType = tryGetTypeAtPosition(longest, i)!; @@ -6845,7 +6844,7 @@ namespace ts { const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter); const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); const hasRestParam = left.hasRestParameter || right.hasRestParameter; - const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes; + const hasLiteralTypes = left.hasLiteralTypes || right.hasLiteralTypes; const result = createSignature( declaration, left.typeParameters || right.typeParameters, From 407054a33e3802a64f79710dc8e33fe678a8bb7c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 14 Dec 2018 16:49:50 -0800 Subject: [PATCH 04/12] Combine parameter names for nicer quick info --- src/compiler/checker.ts | 76 ++++++++++++++++--- ...edUnionsOfDissimilarTyeshaveGoodDisplay.ts | 53 +++++++++++++ 2 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index deba784374c51..424230d413449 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6808,6 +6808,34 @@ namespace ts { return createSymbolWithType(left, thisType); } + function combinePartameterNames(index: number, left: __String | undefined, right: __String | undefined): __String { + if (!left && !right) { + return `arg${index}` as __String; + } + if (!left) { + return right!; + } + if (!right || left === right) { + return left; + } + const names = createMap(); + // We join names in an upperCamelCase fashion. + const seperator = "And"; + const leftNames = unescapeLeadingUnderscores(left).split(seperator); + const rightNames = unescapeLeadingUnderscores(right).split(seperator); + let first = true; + for (const list of [leftNames, rightNames]) { + for (const n of list) { + names.set(first ? (first = false, n) : (n.slice(0, 1).toUpperCase() + n.slice(1)), true); + } + } + if (names.size > 5) { + // Combining >5 names just looks bad + return `arg${index}` as __String; + } + return escapeLeadingUnderscores(arrayFrom(names.keys()).join(seperator)); + } + function combineUnionParameters(left: Signature, right: Signature) { const longest = getParameterCount(left) >= getParameterCount(right) ? left : right; const shorter = longest === left ? right : left; @@ -6825,8 +6853,9 @@ namespace ts { unionParamType = createArrayType(unionParamType); } const isOptional = i > getMinArgumentCount(longest); - // TODO: Synthesize symbol name from input signatures - const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), `arg${i}` as __String); + const leftName = tryGetNameAtPosition(left, i); + const rightName = tryGetNameAtPosition(right, i); + const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), combinePartameterNames(i, leftName, rightName)); paramSymbol.type = unionParamType; params[i] = paramSymbol; } @@ -21202,24 +21231,49 @@ namespace ts { return tryGetTypeAtPosition(signature, pos) || anyType; } - function tryGetTypeAtPosition(signature: Signature, pos: number): Type | undefined { + function tryGetAtXPositionWorker( + signature: Signature, + pos: number, + symbolGetter: (symbol: Symbol) => T, + restTypeGetter: (type: Type, pos: number, paramCount: number) => T): T | undefined { const paramCount = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); if (pos < paramCount) { - return getTypeOfParameter(signature.parameters[pos]); + return symbolGetter(signature.parameters[pos]); } if (signature.hasRestParameter) { const restType = getTypeOfSymbol(signature.parameters[paramCount]); - if (isTupleType(restType)) { - if (pos - paramCount < getLengthOfTupleType(restType)) { - return restType.typeArguments![pos - paramCount]; - } - return getRestTypeOfTupleType(restType); - } - return getIndexTypeOfType(restType, IndexKind.Number); + return restTypeGetter(restType, pos, paramCount); } return undefined; } + function getTupleTypeForArgumentAtPos(restType: Type, pos: number, paramCount: number) { + if (isTupleType(restType)) { + if (pos - paramCount < getLengthOfTupleType(restType)) { + return restType.typeArguments![pos - paramCount]; + } + return getRestTypeOfTupleType(restType); + } + return getIndexTypeOfType(restType, IndexKind.Number); + } + + function tryGetTypeAtPosition(signature: Signature, pos: number): Type | undefined { + return tryGetAtXPositionWorker(signature, pos, getTypeOfParameter, getTupleTypeForArgumentAtPos); + } + + function getTupleParameterNameForArgumentAtPos(restType: Type, pos: number, paramCount: number): __String { + if (isTupleType(restType)) { + if (pos - paramCount < getLengthOfTupleType(restType) && restType.target.associatedNames) { + return restType.target.associatedNames[pos - paramCount]; + } + } + return `arg${pos}` as __String; + } + + function tryGetNameAtPosition(signature: Signature, pos: number): __String | undefined { + return tryGetAtXPositionWorker(signature, pos, s => s.escapedName, getTupleParameterNameForArgumentAtPos); + } + function getRestTypeAtPosition(source: Signature, pos: number): Type { const paramCount = getParameterCount(source); const restType = getEffectiveRestType(source); diff --git a/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts b/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts new file mode 100644 index 0000000000000..42fe9f948cffd --- /dev/null +++ b/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts @@ -0,0 +1,53 @@ +/// + +////declare const callableThing1: +//// | ((o1: {x: number}) => void) +//// | ((o1: {y: number}) => void) +//// ; +//// +////callableThing1(/*1*/); +//// +////declare const callableThing2: +//// | ((o1: {x: number}) => void) +//// | ((o2: {y: number}) => void) +//// ; +//// +////callableThing2(/*2*/); +//// +////declare const callableThing3: +//// | ((o1: {x: number}) => void) +//// | ((o2: {y: number}) => void) +//// | ((o3: {z: number}) => void) +//// | ((o4: {u: number}) => void) +//// | ((o5: {v: number}) => void) +//// ; +//// +////callableThing3(/*3*/); +//// +////declare const callableThing4: +//// | ((o1: {x: number}) => void) +//// | ((o2: {y: number}) => void) +//// | ((o3: {z: number}) => void) +//// | ((o4: {u: number}) => void) +//// | ((o5: {v: number}) => void) +//// | ((o6: {w: number}) => void) +//// ; +//// +////callableThing4(/*4*/); + +verify.signatureHelp({ + marker: "1", + text: "callableThing1(o1: { x: number; } & { y: number; }): void" +}, +{ + marker: "2", + text: "callableThing2(o1AndO2: { x: number; } & { y: number; }): void" +}, +{ + marker: "3", + text: "callableThing3(o1AndO2AndO3AndO4AndO5: { x: number; } & { y: number; } & { z: number; } & { u: number; } & { v: number; }): void" +}, +{ + marker: "4", + text: "callableThing4(arg0: { x: number; } & { y: number; } & { z: number; } & { u: number; } & { v: number; } & { w: number; }): void" +}); From d7c668a83f43343c51daf6650345dec4f861fc44 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 13:10:05 -0800 Subject: [PATCH 05/12] PR feedback 1 --- src/compiler/checker.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 424230d413449..b3b9d549b447c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6733,12 +6733,7 @@ namespace ts { for (let i = 0; i < signatureLists.length; i++) { if (signatureLists[i].length === 0) return emptyArray; if (signatureLists[i].length > 1) { - if (indexWithLengthOverOne === undefined) { - indexWithLengthOverOne = i; - } - else { - indexWithLengthOverOne = -1; // signal there are multiple overload sets - } + indexWithLengthOverOne = indexWithLengthOverOne === undefined ? i : -1; // -1 is a signal there are multiple overload sets } for (const signature of signatureLists[i]) { // Only process signatures with parameter lists that aren't already in the result list @@ -6774,7 +6769,7 @@ namespace ts { if (signatures === masterList) continue; const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = mergeAsUnionSignature(results, signature); + results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, addition)); if (!results) { break; } @@ -6784,13 +6779,6 @@ namespace ts { return result || emptyArray; } - function mergeAsUnionSignature(masterList: Signature[], addition: Signature): Signature[] | undefined { - if (addition.typeParameters && some(masterList, s => !!s.typeParameters)) { - return; // Can't currently unify type parameters - } - return map(masterList, sig => combineSignaturesOfUnionMembers(sig, addition)); - } - function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { if (!left && !right) { return; @@ -6808,7 +6796,7 @@ namespace ts { return createSymbolWithType(left, thisType); } - function combinePartameterNames(index: number, left: __String | undefined, right: __String | undefined): __String { + function combineParameterNames(index: number, left: __String | undefined, right: __String | undefined): __String { if (!left && !right) { return `arg${index}` as __String; } @@ -6855,7 +6843,7 @@ namespace ts { const isOptional = i > getMinArgumentCount(longest); const leftName = tryGetNameAtPosition(left, i); const rightName = tryGetNameAtPosition(right, i); - const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), combinePartameterNames(i, leftName, rightName)); + const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), combineParameterNames(i, leftName, rightName)); paramSymbol.type = unionParamType; params[i] = paramSymbol; } From f24c7f555a955e382d75a27242a44dab93d7d0fe Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 13:24:30 -0800 Subject: [PATCH 06/12] Fix miscopy --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2a867e0562267..5db7e9dc8f408 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6839,7 +6839,7 @@ namespace ts { if (signatures === masterList) continue; const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, addition)); + results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); if (!results) { break; } From d28d87f748ca55661394c5799651e65e67ee762c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 14:32:08 -0800 Subject: [PATCH 07/12] PR feedback round 2 --- src/compiler/checker.ts | 67 +++++++++++------------------------------ 1 file changed, 18 insertions(+), 49 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5db7e9dc8f408..89d7527911a0f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6866,14 +6866,8 @@ namespace ts { return createSymbolWithType(left, thisType); } - function combineParameterNames(index: number, left: __String | undefined, right: __String | undefined): __String { - if (!left && !right) { - return `arg${index}` as __String; - } - if (!left) { - return right!; - } - if (!right || left === right) { + function combineParameterNames(index: number, left: __String, right: __String): __String { + if (left === right) { return left; } const names = createMap(); @@ -6901,26 +6895,22 @@ namespace ts { const eitherHasEffectiveRest = (hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right)); const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); - let i = 0; - for (; i < longestCount; i++) { + for (let i = 0; i < longestCount; i++) { const longestParamType = tryGetTypeAtPosition(longest, i)!; const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; - let unionParamType = getIntersectionType([longestParamType, shorterParamType]); + const unionParamType = getIntersectionType([longestParamType, shorterParamType]); const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); - if (isRestParam) { - unionParamType = createArrayType(unionParamType); - } - const isOptional = i > getMinArgumentCount(longest); - const leftName = tryGetNameAtPosition(left, i); - const rightName = tryGetNameAtPosition(right, i); + const isOptional = i > getMinArgumentCount(longest) && i > getMinArgumentCount(shorter); + const leftName = getParameterNameAtPosition(left, i); + const rightName = getParameterNameAtPosition(right, i); const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), combineParameterNames(i, leftName, rightName)); - paramSymbol.type = unionParamType; + paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType; params[i] = paramSymbol; } if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); - restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, i)); - params[i] = restParamSymbol; + restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); + params[longestCount] = restParamSymbol; } return params; } @@ -21252,22 +21242,6 @@ namespace ts { return tryGetTypeAtPosition(signature, pos) || anyType; } - function tryGetAtXPositionWorker( - signature: Signature, - pos: number, - symbolGetter: (symbol: Symbol) => T, - restTypeGetter: (type: Type, pos: number, paramCount: number) => T): T | undefined { - const paramCount = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); - if (pos < paramCount) { - return symbolGetter(signature.parameters[pos]); - } - if (signature.hasRestParameter) { - const restType = getTypeOfSymbol(signature.parameters[paramCount]); - return restTypeGetter(restType, pos, paramCount); - } - return undefined; - } - function getTupleTypeForArgumentAtPos(restType: Type, pos: number, paramCount: number) { if (isTupleType(restType)) { if (pos - paramCount < getLengthOfTupleType(restType)) { @@ -21279,20 +21253,15 @@ namespace ts { } function tryGetTypeAtPosition(signature: Signature, pos: number): Type | undefined { - return tryGetAtXPositionWorker(signature, pos, getTypeOfParameter, getTupleTypeForArgumentAtPos); - } - - function getTupleParameterNameForArgumentAtPos(restType: Type, pos: number, paramCount: number): __String { - if (isTupleType(restType)) { - if (pos - paramCount < getLengthOfTupleType(restType) && restType.target.associatedNames) { - return restType.target.associatedNames[pos - paramCount]; - } + const paramCount = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); + if (pos < paramCount) { + return getTypeOfParameter(signature.parameters[pos]); } - return `arg${pos}` as __String; - } - - function tryGetNameAtPosition(signature: Signature, pos: number): __String | undefined { - return tryGetAtXPositionWorker(signature, pos, s => s.escapedName, getTupleParameterNameForArgumentAtPos); + if (signature.hasRestParameter) { + const restType = getTypeOfSymbol(signature.parameters[paramCount]); + return getTupleTypeForArgumentAtPos(restType, pos, paramCount); + } + return undefined; } function getRestTypeAtPosition(source: Signature, pos: number): Type { From f1d78e9e1496c8f3ed8a3ca199340d50c95f6ddd Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 15:02:55 -0800 Subject: [PATCH 08/12] Remove argument name combining because loc :( --- src/compiler/checker.ts | 17 +---------------- ...lledUnionsOfDissimilarTyeshaveGoodDisplay.ts | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89d7527911a0f..0258757b341d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6870,22 +6870,7 @@ namespace ts { if (left === right) { return left; } - const names = createMap(); - // We join names in an upperCamelCase fashion. - const seperator = "And"; - const leftNames = unescapeLeadingUnderscores(left).split(seperator); - const rightNames = unescapeLeadingUnderscores(right).split(seperator); - let first = true; - for (const list of [leftNames, rightNames]) { - for (const n of list) { - names.set(first ? (first = false, n) : (n.slice(0, 1).toUpperCase() + n.slice(1)), true); - } - } - if (names.size > 5) { - // Combining >5 names just looks bad - return `arg${index}` as __String; - } - return escapeLeadingUnderscores(arrayFrom(names.keys()).join(seperator)); + return `arg${index}` as __String; } function combineUnionParameters(left: Signature, right: Signature) { diff --git a/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts b/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts index 42fe9f948cffd..e8c42871b6748 100644 --- a/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts +++ b/tests/cases/fourslash/calledUnionsOfDissimilarTyeshaveGoodDisplay.ts @@ -41,11 +41,11 @@ verify.signatureHelp({ }, { marker: "2", - text: "callableThing2(o1AndO2: { x: number; } & { y: number; }): void" + text: "callableThing2(arg0: { x: number; } & { y: number; }): void" }, { marker: "3", - text: "callableThing3(o1AndO2AndO3AndO4AndO5: { x: number; } & { y: number; } & { z: number; } & { u: number; } & { v: number; }): void" + text: "callableThing3(arg0: { x: number; } & { y: number; } & { z: number; } & { u: number; } & { v: number; }): void" }, { marker: "4", From 9c7abc4b477daf659ee25fb0e1a043d246d6c4ca Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 15:27:47 -0800 Subject: [PATCH 09/12] Nit cleanup round 3 --- src/compiler/checker.ts | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0258757b341d5..bc8fc864e08d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6836,12 +6836,13 @@ namespace ts { const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0]; let results: Signature[] | undefined = masterList.slice(); for (const signatures of signatureLists) { - if (signatures === masterList) continue; - const signature = signatures[0]; - Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); - results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); - if (!results) { - break; + if (signatures !== masterList) { + const signature = signatures[0]; + Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); + results = signature.typeParameters && some(results, s => !!s.typeParameters) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); + if (!results) { + break; + } } } result = results; @@ -6853,11 +6854,8 @@ namespace ts { if (!left && !right) { return; } - if (!left) { - return right; - } - if (!right) { - return left; + if (!left || !right) { + return left || right; } // A signature `this` type might be a read or a write position... It's very possible that it should be invariant // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be @@ -6866,13 +6864,6 @@ namespace ts { return createSymbolWithType(left, thisType); } - function combineParameterNames(index: number, left: __String, right: __String): __String { - if (left === right) { - return left; - } - return `arg${index}` as __String; - } - function combineUnionParameters(left: Signature, right: Signature) { const longest = getParameterCount(left) >= getParameterCount(right) ? left : right; const shorter = longest === left ? right : left; @@ -6888,7 +6879,10 @@ namespace ts { const isOptional = i > getMinArgumentCount(longest) && i > getMinArgumentCount(shorter); const leftName = getParameterNameAtPosition(left, i); const rightName = getParameterNameAtPosition(right, i); - const paramSymbol = createSymbol(SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), combineParameterNames(i, leftName, rightName)); + const paramSymbol = createSymbol( + SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), + leftName === rightName ? leftName : `arg${i}` as __String + ); paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType; params[i] = paramSymbol; } From 2ef673390b11f21e8733b0aee7e8259a57a1f8bf Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 15:32:24 -0800 Subject: [PATCH 10/12] Reinline getTupleTypeForArgumentAtPos --- src/compiler/checker.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc8fc864e08d2..f04e6dc9a5cda 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21221,16 +21221,6 @@ namespace ts { return tryGetTypeAtPosition(signature, pos) || anyType; } - function getTupleTypeForArgumentAtPos(restType: Type, pos: number, paramCount: number) { - if (isTupleType(restType)) { - if (pos - paramCount < getLengthOfTupleType(restType)) { - return restType.typeArguments![pos - paramCount]; - } - return getRestTypeOfTupleType(restType); - } - return getIndexTypeOfType(restType, IndexKind.Number); - } - function tryGetTypeAtPosition(signature: Signature, pos: number): Type | undefined { const paramCount = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); if (pos < paramCount) { @@ -21238,7 +21228,13 @@ namespace ts { } if (signature.hasRestParameter) { const restType = getTypeOfSymbol(signature.parameters[paramCount]); - return getTupleTypeForArgumentAtPos(restType, pos, paramCount); + if (isTupleType(restType)) { + if (pos - paramCount < getLengthOfTupleType(restType)) { + return restType.typeArguments![pos - paramCount]; + } + return getRestTypeOfTupleType(restType); + } + return getIndexTypeOfType(restType, IndexKind.Number); } return undefined; } From 875bb0e7c69ea69687130dcdbc7708509cadc88c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 16:11:04 -0800 Subject: [PATCH 11/12] Remove a tad more --- src/compiler/checker.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f04e6dc9a5cda..4449c0f3aa589 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6851,9 +6851,6 @@ namespace ts { } function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined { - if (!left && !right) { - return; - } if (!left || !right) { return left || right; } From 7b2ad7f8aab947241cb5cc24f9798f289fd705c3 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Dec 2018 16:14:25 -0800 Subject: [PATCH 12/12] No step on sneky off-by-one error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4449c0f3aa589..23c5568f2e5f4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6873,7 +6873,7 @@ namespace ts { const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; const unionParamType = getIntersectionType([longestParamType, shorterParamType]); const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); - const isOptional = i > getMinArgumentCount(longest) && i > getMinArgumentCount(shorter); + const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); const leftName = getParameterNameAtPosition(left, i); const rightName = getParameterNameAtPosition(right, i); const paramSymbol = createSymbol(