mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 11:50:54 -06:00
Allow identical type parameter lists to merge in union signatures (#31023)
* Have signature identity checks look at the base constraint of type parameters, allow identical type parameter lists to merge in union signatures * Update text in fourslash test * Add whitespace to fix lint, remove duplicate impl * Consolidate names * Remove comparisons of type parameter defaults, add more test cases
This commit is contained in:
parent
675cd4d7ce
commit
b217f22e79
@ -10274,7 +10274,7 @@ namespace ts {
|
||||
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));
|
||||
results = signature.typeParameters && some(results, s => !!s.typeParameters && !compareTypeParametersIdentical(signature.typeParameters!, s.typeParameters)) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature));
|
||||
if (!results) {
|
||||
break;
|
||||
}
|
||||
@ -10285,18 +10285,39 @@ namespace ts {
|
||||
return result || emptyArray;
|
||||
}
|
||||
|
||||
function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined): Symbol | undefined {
|
||||
function compareTypeParametersIdentical(sourceParams: readonly TypeParameter[], targetParams: readonly TypeParameter[]): boolean {
|
||||
if (sourceParams.length !== targetParams.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mapper = createTypeMapper(targetParams, sourceParams);
|
||||
for (let i = 0; i < sourceParams.length; i++) {
|
||||
const source = sourceParams[i];
|
||||
const target = targetParams[i];
|
||||
if (source === target) continue;
|
||||
// We instantiate the target type parameter constraints into the source types so we can recognize `<T, U extends T>` as the same as `<A, B extends A>`
|
||||
if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false;
|
||||
// We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match.
|
||||
// It might make sense to combine these defaults in the future, but doing so intelligently requires knowing
|
||||
// if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type)
|
||||
// and, since it's just an inference _default_, just picking one arbitrarily works OK.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined, mapper: TypeMapper | undefined): Symbol | undefined {
|
||||
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
|
||||
// permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures.
|
||||
const thisType = getIntersectionType([getTypeOfSymbol(left), getTypeOfSymbol(right)]);
|
||||
const thisType = getIntersectionType([getTypeOfSymbol(left), instantiateType(getTypeOfSymbol(right), mapper)]);
|
||||
return createSymbolWithType(left, thisType);
|
||||
}
|
||||
|
||||
function combineUnionParameters(left: Signature, right: Signature) {
|
||||
function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined) {
|
||||
const leftCount = getParameterCount(left);
|
||||
const rightCount = getParameterCount(right);
|
||||
const longest = leftCount >= rightCount ? left : right;
|
||||
@ -10306,8 +10327,14 @@ namespace ts {
|
||||
const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest);
|
||||
const params = new Array<Symbol>(longestCount + (needsExtraRestElement ? 1 : 0));
|
||||
for (let i = 0; i < longestCount; i++) {
|
||||
const longestParamType = tryGetTypeAtPosition(longest, i)!;
|
||||
const shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType;
|
||||
let longestParamType = tryGetTypeAtPosition(longest, i)!;
|
||||
if (longest === right) {
|
||||
longestParamType = instantiateType(longestParamType, mapper);
|
||||
}
|
||||
let shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType;
|
||||
if (shorter === right) {
|
||||
shorterParamType = instantiateType(shorterParamType, mapper);
|
||||
}
|
||||
const unionParamType = getIntersectionType([longestParamType, shorterParamType]);
|
||||
const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1);
|
||||
const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter);
|
||||
@ -10328,19 +10355,28 @@ namespace ts {
|
||||
if (needsExtraRestElement) {
|
||||
const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String);
|
||||
restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount));
|
||||
if (shorter === right) {
|
||||
restParamSymbol.type = instantiateType(restParamSymbol.type, mapper);
|
||||
}
|
||||
params[longestCount] = restParamSymbol;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature {
|
||||
const typeParams = left.typeParameters || right.typeParameters;
|
||||
let paramMapper: TypeMapper | undefined;
|
||||
if (left.typeParameters && right.typeParameters) {
|
||||
paramMapper = createTypeMapper(right.typeParameters, left.typeParameters);
|
||||
// We just use the type parameter defaults from the first signature
|
||||
}
|
||||
const declaration = left.declaration;
|
||||
const params = combineUnionParameters(left, right);
|
||||
const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter);
|
||||
const params = combineUnionParameters(left, right, paramMapper);
|
||||
const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper);
|
||||
const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount);
|
||||
const result = createSignature(
|
||||
declaration,
|
||||
left.typeParameters || right.typeParameters,
|
||||
typeParams,
|
||||
thisParam,
|
||||
params,
|
||||
/*resolvedReturnType*/ undefined,
|
||||
@ -10349,6 +10385,9 @@ namespace ts {
|
||||
(left.flags | right.flags) & SignatureFlags.PropagatingFlags
|
||||
);
|
||||
result.unionSignatures = concatenate(left.unionSignatures || [left], [right]);
|
||||
if (paramMapper) {
|
||||
result.mapper = left.mapper && left.unionSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -11882,7 +11921,7 @@ namespace ts {
|
||||
return errorType;
|
||||
}
|
||||
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) :
|
||||
signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
|
||||
signature.unionSignatures ? instantiateType(getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype), signature.mapper) :
|
||||
getReturnTypeFromAnnotation(signature.declaration!) ||
|
||||
(nodeIsMissing((<FunctionLikeDeclaration>signature.declaration).body) ? anyType : getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration));
|
||||
if (signature.flags & SignatureFlags.IsInnerCallChain) {
|
||||
|
||||
85
tests/baselines/reference/unionOfClassCalls.errors.txt
Normal file
85
tests/baselines/reference/unionOfClassCalls.errors.txt
Normal file
@ -0,0 +1,85 @@
|
||||
tests/cases/compiler/unionOfClassCalls.ts(28,5): error TS2349: This expression is not callable.
|
||||
Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other.
|
||||
|
||||
|
||||
==== tests/cases/compiler/unionOfClassCalls.ts (1 errors) ====
|
||||
// from https://github.com/microsoft/TypeScript/issues/30717
|
||||
declare class Test<T> {
|
||||
obj: T;
|
||||
get<K extends keyof T>(k: K): T[K];
|
||||
}
|
||||
|
||||
interface A { t: "A" }
|
||||
interface B { t: "B" }
|
||||
|
||||
declare const tmp: Test<A> | Test<B>;
|
||||
|
||||
switch (tmp.get('t')) {
|
||||
case 'A': break;
|
||||
case 'B': break;
|
||||
}
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
const arr: number[] | string[] = []; // Works with Array<number | string>
|
||||
const arr1: number[] = [];
|
||||
const arr2: string[] = [];
|
||||
|
||||
arr.map((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
|
||||
~~~~~~
|
||||
!!! error TS2349: This expression is not callable.
|
||||
!!! error TS2349: Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other.
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr.forEach((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.map((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.reduce((acc: number[], a: number, index: number) => {
|
||||
return [a]
|
||||
}, [])
|
||||
|
||||
arr1.forEach((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
arr2.map((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr2.reduce((acc: string[], a: string, index: number) => {
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr2.forEach((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36307
|
||||
|
||||
declare class Foo {
|
||||
doThing(): Promise<this>
|
||||
}
|
||||
|
||||
declare class Bar extends Foo {
|
||||
bar: number;
|
||||
}
|
||||
declare class Baz extends Foo {
|
||||
baz: number;
|
||||
}
|
||||
|
||||
declare var a: Bar | Baz;
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then((result: Bar | Baz) => {
|
||||
// whatever
|
||||
});
|
||||
|
||||
121
tests/baselines/reference/unionOfClassCalls.js
Normal file
121
tests/baselines/reference/unionOfClassCalls.js
Normal file
@ -0,0 +1,121 @@
|
||||
//// [unionOfClassCalls.ts]
|
||||
// from https://github.com/microsoft/TypeScript/issues/30717
|
||||
declare class Test<T> {
|
||||
obj: T;
|
||||
get<K extends keyof T>(k: K): T[K];
|
||||
}
|
||||
|
||||
interface A { t: "A" }
|
||||
interface B { t: "B" }
|
||||
|
||||
declare const tmp: Test<A> | Test<B>;
|
||||
|
||||
switch (tmp.get('t')) {
|
||||
case 'A': break;
|
||||
case 'B': break;
|
||||
}
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
const arr: number[] | string[] = []; // Works with Array<number | string>
|
||||
const arr1: number[] = [];
|
||||
const arr2: string[] = [];
|
||||
|
||||
arr.map((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr.forEach((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.map((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.reduce((acc: number[], a: number, index: number) => {
|
||||
return [a]
|
||||
}, [])
|
||||
|
||||
arr1.forEach((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
arr2.map((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr2.reduce((acc: string[], a: string, index: number) => {
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr2.forEach((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36307
|
||||
|
||||
declare class Foo {
|
||||
doThing(): Promise<this>
|
||||
}
|
||||
|
||||
declare class Bar extends Foo {
|
||||
bar: number;
|
||||
}
|
||||
declare class Baz extends Foo {
|
||||
baz: number;
|
||||
}
|
||||
|
||||
declare var a: Bar | Baz;
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then((result: Bar | Baz) => {
|
||||
// whatever
|
||||
});
|
||||
|
||||
|
||||
//// [unionOfClassCalls.js]
|
||||
"use strict";
|
||||
switch (tmp.get('t')) {
|
||||
case 'A': break;
|
||||
case 'B': break;
|
||||
}
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
var arr = []; // Works with Array<number | string>
|
||||
var arr1 = [];
|
||||
var arr2 = [];
|
||||
arr.map(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce(function (acc, a, index) {
|
||||
return [];
|
||||
}, []);
|
||||
arr.forEach(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
arr1.map(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
arr1.reduce(function (acc, a, index) {
|
||||
return [a];
|
||||
}, []);
|
||||
arr1.forEach(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
arr2.map(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
arr2.reduce(function (acc, a, index) {
|
||||
return [];
|
||||
}, []);
|
||||
arr2.forEach(function (a, index) {
|
||||
return index;
|
||||
});
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then(function (result) {
|
||||
// whatever
|
||||
});
|
||||
207
tests/baselines/reference/unionOfClassCalls.symbols
Normal file
207
tests/baselines/reference/unionOfClassCalls.symbols
Normal file
@ -0,0 +1,207 @@
|
||||
=== tests/cases/compiler/unionOfClassCalls.ts ===
|
||||
// from https://github.com/microsoft/TypeScript/issues/30717
|
||||
declare class Test<T> {
|
||||
>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19))
|
||||
|
||||
obj: T;
|
||||
>obj : Symbol(Test.obj, Decl(unionOfClassCalls.ts, 1, 23))
|
||||
>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19))
|
||||
|
||||
get<K extends keyof T>(k: K): T[K];
|
||||
>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11))
|
||||
>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8))
|
||||
>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19))
|
||||
>k : Symbol(k, Decl(unionOfClassCalls.ts, 3, 27))
|
||||
>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8))
|
||||
>T : Symbol(T, Decl(unionOfClassCalls.ts, 1, 19))
|
||||
>K : Symbol(K, Decl(unionOfClassCalls.ts, 3, 8))
|
||||
}
|
||||
|
||||
interface A { t: "A" }
|
||||
>A : Symbol(A, Decl(unionOfClassCalls.ts, 4, 1))
|
||||
>t : Symbol(A.t, Decl(unionOfClassCalls.ts, 6, 13))
|
||||
|
||||
interface B { t: "B" }
|
||||
>B : Symbol(B, Decl(unionOfClassCalls.ts, 6, 22))
|
||||
>t : Symbol(B.t, Decl(unionOfClassCalls.ts, 7, 13))
|
||||
|
||||
declare const tmp: Test<A> | Test<B>;
|
||||
>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 9, 13))
|
||||
>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0))
|
||||
>A : Symbol(A, Decl(unionOfClassCalls.ts, 4, 1))
|
||||
>Test : Symbol(Test, Decl(unionOfClassCalls.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(unionOfClassCalls.ts, 6, 22))
|
||||
|
||||
switch (tmp.get('t')) {
|
||||
>tmp.get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11), Decl(unionOfClassCalls.ts, 2, 11))
|
||||
>tmp : Symbol(tmp, Decl(unionOfClassCalls.ts, 9, 13))
|
||||
>get : Symbol(Test.get, Decl(unionOfClassCalls.ts, 2, 11), Decl(unionOfClassCalls.ts, 2, 11))
|
||||
|
||||
case 'A': break;
|
||||
case 'B': break;
|
||||
}
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
const arr: number[] | string[] = []; // Works with Array<number | string>
|
||||
>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5))
|
||||
|
||||
const arr1: number[] = [];
|
||||
>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5))
|
||||
|
||||
const arr2: string[] = [];
|
||||
>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5))
|
||||
|
||||
arr.map((a: number | string, index: number) => {
|
||||
>arr.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 22, 9))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 22, 28))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 22, 28))
|
||||
|
||||
})
|
||||
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
|
||||
>arr.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more)
|
||||
>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5))
|
||||
>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more)
|
||||
>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 27, 12))
|
||||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 27, 31))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 27, 51))
|
||||
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr.forEach((a: number | string, index: number) => {
|
||||
>arr.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(unionOfClassCalls.ts, 18, 5))
|
||||
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 31, 13))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 31, 32))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 31, 32))
|
||||
|
||||
})
|
||||
|
||||
arr1.map((a: number, index: number) => {
|
||||
>arr1.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 35, 10))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 35, 20))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 35, 20))
|
||||
|
||||
})
|
||||
|
||||
arr1.reduce((acc: number[], a: number, index: number) => {
|
||||
>arr1.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5))
|
||||
>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 39, 13))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 39, 27))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 39, 38))
|
||||
|
||||
return [a]
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 39, 27))
|
||||
|
||||
}, [])
|
||||
|
||||
arr1.forEach((a: number, index: number) => {
|
||||
>arr1.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
|
||||
>arr1 : Symbol(arr1, Decl(unionOfClassCalls.ts, 19, 5))
|
||||
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 43, 14))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 43, 24))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 43, 24))
|
||||
|
||||
})
|
||||
arr2.map((a: string, index: number) => {
|
||||
>arr2.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 46, 10))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 46, 21))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 46, 21))
|
||||
|
||||
})
|
||||
|
||||
arr2.reduce((acc: string[], a: string, index: number) => {
|
||||
>arr2.reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5))
|
||||
>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>acc : Symbol(acc, Decl(unionOfClassCalls.ts, 50, 13))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 50, 27))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 50, 38))
|
||||
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr2.forEach((a: string, index: number) => {
|
||||
>arr2.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
|
||||
>arr2 : Symbol(arr2, Decl(unionOfClassCalls.ts, 20, 5))
|
||||
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 54, 14))
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 54, 24))
|
||||
|
||||
return index
|
||||
>index : Symbol(index, Decl(unionOfClassCalls.ts, 54, 24))
|
||||
|
||||
})
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36307
|
||||
|
||||
declare class Foo {
|
||||
>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2))
|
||||
|
||||
doThing(): Promise<this>
|
||||
>doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
declare class Bar extends Foo {
|
||||
>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1))
|
||||
>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2))
|
||||
|
||||
bar: number;
|
||||
>bar : Symbol(Bar.bar, Decl(unionOfClassCalls.ts, 64, 31))
|
||||
}
|
||||
declare class Baz extends Foo {
|
||||
>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1))
|
||||
>Foo : Symbol(Foo, Decl(unionOfClassCalls.ts, 56, 2))
|
||||
|
||||
baz: number;
|
||||
>baz : Symbol(Baz.baz, Decl(unionOfClassCalls.ts, 67, 31))
|
||||
}
|
||||
|
||||
declare var a: Bar | Baz;
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 71, 11))
|
||||
>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1))
|
||||
>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1))
|
||||
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then((result: Bar | Baz) => {
|
||||
>a.doThing().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>a.doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19), Decl(unionOfClassCalls.ts, 60, 19))
|
||||
>a : Symbol(a, Decl(unionOfClassCalls.ts, 71, 11))
|
||||
>doThing : Symbol(Foo.doThing, Decl(unionOfClassCalls.ts, 60, 19), Decl(unionOfClassCalls.ts, 60, 19))
|
||||
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>result : Symbol(result, Decl(unionOfClassCalls.ts, 73, 18))
|
||||
>Bar : Symbol(Bar, Decl(unionOfClassCalls.ts, 62, 1))
|
||||
>Baz : Symbol(Baz, Decl(unionOfClassCalls.ts, 66, 1))
|
||||
|
||||
// whatever
|
||||
});
|
||||
|
||||
225
tests/baselines/reference/unionOfClassCalls.types
Normal file
225
tests/baselines/reference/unionOfClassCalls.types
Normal file
@ -0,0 +1,225 @@
|
||||
=== tests/cases/compiler/unionOfClassCalls.ts ===
|
||||
// from https://github.com/microsoft/TypeScript/issues/30717
|
||||
declare class Test<T> {
|
||||
>Test : Test<T>
|
||||
|
||||
obj: T;
|
||||
>obj : T
|
||||
|
||||
get<K extends keyof T>(k: K): T[K];
|
||||
>get : <K extends keyof T>(k: K) => T[K]
|
||||
>k : K
|
||||
}
|
||||
|
||||
interface A { t: "A" }
|
||||
>t : "A"
|
||||
|
||||
interface B { t: "B" }
|
||||
>t : "B"
|
||||
|
||||
declare const tmp: Test<A> | Test<B>;
|
||||
>tmp : Test<A> | Test<B>
|
||||
|
||||
switch (tmp.get('t')) {
|
||||
>tmp.get('t') : "A" | "B"
|
||||
>tmp.get : (<K extends "t">(k: K) => A[K]) | (<K extends "t">(k: K) => B[K])
|
||||
>tmp : Test<A> | Test<B>
|
||||
>get : (<K extends "t">(k: K) => A[K]) | (<K extends "t">(k: K) => B[K])
|
||||
>'t' : "t"
|
||||
|
||||
case 'A': break;
|
||||
>'A' : "A"
|
||||
|
||||
case 'B': break;
|
||||
>'B' : "B"
|
||||
}
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
const arr: number[] | string[] = []; // Works with Array<number | string>
|
||||
>arr : number[] | string[]
|
||||
>[] : never[]
|
||||
|
||||
const arr1: number[] = [];
|
||||
>arr1 : number[]
|
||||
>[] : never[]
|
||||
|
||||
const arr2: string[] = [];
|
||||
>arr2 : string[]
|
||||
>[] : never[]
|
||||
|
||||
arr.map((a: number | string, index: number) => {
|
||||
>arr.map((a: number | string, index: number) => { return index}) : number[]
|
||||
>arr.map : (<U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[])
|
||||
>arr : number[] | string[]
|
||||
>map : (<U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[])
|
||||
>(a: number | string, index: number) => { return index} : (a: number | string, index: number) => number
|
||||
>a : string | number
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
|
||||
>arr.reduce((acc: Array<string>, a: number | string, index: number) => { return []}, []) : any
|
||||
>arr.reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }
|
||||
>arr : number[] | string[]
|
||||
>reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }
|
||||
>(acc: Array<string>, a: number | string, index: number) => { return []} : (acc: Array<string>, a: number | string, index: number) => never[]
|
||||
>acc : string[]
|
||||
>a : string | number
|
||||
>index : number
|
||||
|
||||
return []
|
||||
>[] : never[]
|
||||
|
||||
}, [])
|
||||
>[] : never[]
|
||||
|
||||
arr.forEach((a: number | string, index: number) => {
|
||||
>arr.forEach((a: number | string, index: number) => { return index}) : void
|
||||
>arr.forEach : ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) | ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void)
|
||||
>arr : number[] | string[]
|
||||
>forEach : ((callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void) | ((callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void)
|
||||
>(a: number | string, index: number) => { return index} : (a: number | string, index: number) => number
|
||||
>a : string | number
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
|
||||
arr1.map((a: number, index: number) => {
|
||||
>arr1.map((a: number, index: number) => { return index}) : number[]
|
||||
>arr1.map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>arr1 : number[]
|
||||
>map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>(a: number, index: number) => { return index} : (a: number, index: number) => number
|
||||
>a : number
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
|
||||
arr1.reduce((acc: number[], a: number, index: number) => {
|
||||
>arr1.reduce((acc: number[], a: number, index: number) => { return [a]}, []) : number[]
|
||||
>arr1.reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }
|
||||
>arr1 : number[]
|
||||
>reduce : { (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; }
|
||||
>(acc: number[], a: number, index: number) => { return [a]} : (acc: number[], a: number, index: number) => number[]
|
||||
>acc : number[]
|
||||
>a : number
|
||||
>index : number
|
||||
|
||||
return [a]
|
||||
>[a] : number[]
|
||||
>a : number
|
||||
|
||||
}, [])
|
||||
>[] : never[]
|
||||
|
||||
arr1.forEach((a: number, index: number) => {
|
||||
>arr1.forEach((a: number, index: number) => { return index}) : void
|
||||
>arr1.forEach : (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void
|
||||
>arr1 : number[]
|
||||
>forEach : (callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any) => void
|
||||
>(a: number, index: number) => { return index} : (a: number, index: number) => number
|
||||
>a : number
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
arr2.map((a: string, index: number) => {
|
||||
>arr2.map((a: string, index: number) => { return index}) : number[]
|
||||
>arr2.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
|
||||
>arr2 : string[]
|
||||
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
|
||||
>(a: string, index: number) => { return index} : (a: string, index: number) => number
|
||||
>a : string
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
|
||||
arr2.reduce((acc: string[], a: string, index: number) => {
|
||||
>arr2.reduce((acc: string[], a: string, index: number) => { return []}, []) : never[]
|
||||
>arr2.reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }
|
||||
>arr2 : string[]
|
||||
>reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }
|
||||
>(acc: string[], a: string, index: number) => { return []} : (acc: string[], a: string, index: number) => never[]
|
||||
>acc : string[]
|
||||
>a : string
|
||||
>index : number
|
||||
|
||||
return []
|
||||
>[] : never[]
|
||||
|
||||
}, [])
|
||||
>[] : never[]
|
||||
|
||||
arr2.forEach((a: string, index: number) => {
|
||||
>arr2.forEach((a: string, index: number) => { return index}) : void
|
||||
>arr2.forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void
|
||||
>arr2 : string[]
|
||||
>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void
|
||||
>(a: string, index: number) => { return index} : (a: string, index: number) => number
|
||||
>a : string
|
||||
>index : number
|
||||
|
||||
return index
|
||||
>index : number
|
||||
|
||||
})
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36307
|
||||
|
||||
declare class Foo {
|
||||
>Foo : Foo
|
||||
|
||||
doThing(): Promise<this>
|
||||
>doThing : () => Promise<this>
|
||||
}
|
||||
|
||||
declare class Bar extends Foo {
|
||||
>Bar : Bar
|
||||
>Foo : Foo
|
||||
|
||||
bar: number;
|
||||
>bar : number
|
||||
}
|
||||
declare class Baz extends Foo {
|
||||
>Baz : Baz
|
||||
>Foo : Foo
|
||||
|
||||
baz: number;
|
||||
>baz : number
|
||||
}
|
||||
|
||||
declare var a: Bar | Baz;
|
||||
>a : Bar | Baz
|
||||
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then((result: Bar | Baz) => {
|
||||
>a.doThing().then((result: Bar | Baz) => { // whatever}) : Promise<void>
|
||||
>a.doThing().then : (<TResult1 = Bar, TResult2 = never>(onfulfilled?: ((value: Bar) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>) | (<TResult1 = Baz, TResult2 = never>(onfulfilled?: ((value: Baz) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>)
|
||||
>a.doThing() : Promise<Bar> | Promise<Baz>
|
||||
>a.doThing : (() => Promise<Bar>) | (() => Promise<Baz>)
|
||||
>a : Bar | Baz
|
||||
>doThing : (() => Promise<Bar>) | (() => Promise<Baz>)
|
||||
>then : (<TResult1 = Bar, TResult2 = never>(onfulfilled?: ((value: Bar) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>) | (<TResult1 = Baz, TResult2 = never>(onfulfilled?: ((value: Baz) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>)
|
||||
>(result: Bar | Baz) => { // whatever} : (result: Bar | Baz) => void
|
||||
>result : Bar | Baz
|
||||
|
||||
// whatever
|
||||
});
|
||||
|
||||
77
tests/cases/compiler/unionOfClassCalls.ts
Normal file
77
tests/cases/compiler/unionOfClassCalls.ts
Normal file
@ -0,0 +1,77 @@
|
||||
// @strict: true
|
||||
// from https://github.com/microsoft/TypeScript/issues/30717
|
||||
declare class Test<T> {
|
||||
obj: T;
|
||||
get<K extends keyof T>(k: K): T[K];
|
||||
}
|
||||
|
||||
interface A { t: "A" }
|
||||
interface B { t: "B" }
|
||||
|
||||
declare const tmp: Test<A> | Test<B>;
|
||||
|
||||
switch (tmp.get('t')) {
|
||||
case 'A': break;
|
||||
case 'B': break;
|
||||
}
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
const arr: number[] | string[] = []; // Works with Array<number | string>
|
||||
const arr1: number[] = [];
|
||||
const arr2: string[] = [];
|
||||
|
||||
arr.map((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// This case still doesn't work because `reduce` has multiple overloads :(
|
||||
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr.forEach((a: number | string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.map((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr1.reduce((acc: number[], a: number, index: number) => {
|
||||
return [a]
|
||||
}, [])
|
||||
|
||||
arr1.forEach((a: number, index: number) => {
|
||||
return index
|
||||
})
|
||||
arr2.map((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
arr2.reduce((acc: string[], a: string, index: number) => {
|
||||
return []
|
||||
}, [])
|
||||
|
||||
arr2.forEach((a: string, index: number) => {
|
||||
return index
|
||||
})
|
||||
|
||||
// from https://github.com/microsoft/TypeScript/issues/36307
|
||||
|
||||
declare class Foo {
|
||||
doThing(): Promise<this>
|
||||
}
|
||||
|
||||
declare class Bar extends Foo {
|
||||
bar: number;
|
||||
}
|
||||
declare class Baz extends Foo {
|
||||
baz: number;
|
||||
}
|
||||
|
||||
declare var a: Bar | Baz;
|
||||
// note, you must annotate `result` for now
|
||||
a.doThing().then((result: Bar | Baz) => {
|
||||
// whatever
|
||||
});
|
||||
@ -3,7 +3,7 @@
|
||||
////var y: Array<string>|Array<number>;
|
||||
////y.map/**/(
|
||||
|
||||
const text = "(property) Array<T>.map: (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[])";
|
||||
const text = "(method) Array<T>.map<unknown>(callbackfn: ((value: string, index: number, array: string[]) => unknown) & ((value: number, index: number, array: number[]) => unknown), thisArg: any): unknown[]";
|
||||
const documentation = "Calls a defined callback function on each element of an array, and returns an array that contains the results.";
|
||||
|
||||
verify.quickInfoAt("", text, documentation);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user