Skip to content

Commit

Permalink
Fix microsoft#55632 (don't use isTupleLikeType, use map/filter instea…
Browse files Browse the repository at this point in the history
…d of .map/.filter, add new test cases)
  • Loading branch information
fwolff committed Dec 2, 2024
1 parent 27b9fbf commit 63553c4
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 10 deletions.
29 changes: 20 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32029,16 +32029,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
}

function areTupleLikeProperties(properties: Symbol[]): boolean {
return properties.length > 0 && every(properties, p => p.escapedName === "length" as __String || /^\d+$/.test(p.escapedName.toString()));
}

function discriminateContextualTypeByArrayElements(node: ArrayLiteralExpression, contextualType: UnionType) {
const key = `D${getNodeId(node)},${getTypeId(contextualType)}`;
const cachedType = getCachedType(key);
if (cachedType) return cachedType;

const elementsLength = node.elements.length;
const filteredType = filterType(contextualType, type => {
if (!isTupleLikeType(type)) return true;
if (isTupleType(type)) return elementsLength >= type.target.minLength && (!!(type.target.combinedFlags & ElementFlags.Variable) || elementsLength <= type.target.fixedLength);
if (isTupleType(type)) {
return elementsLength >= type.target.minLength && (!!(type.target.combinedFlags & ElementFlags.Variable) || elementsLength <= type.target.fixedLength);
}
const properties = getPropertiesOfType(type);
if (!areTupleLikeProperties(properties)) return true;
if (elementsLength > properties.length || elementsLength < properties.reduce((c, p) => p.flags & SymbolFlags.Optional ? c : ++c, 0)) return false;
for (let i = 0; i < elementsLength; i++) {
if (!some(properties, p => p.escapedName === ("" + i) as __String)) return false;
Expand All @@ -32047,18 +32053,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
});

if (filteredType.flags & TypeFlags.Never) return setCachedType(key, contextualType);
if (!(filteredType.flags & TypeFlags.Union)) return setCachedType(key, isTupleLikeType(filteredType) ? filteredType : contextualType);
if (!(filteredType.flags & TypeFlags.Union)) {
return setCachedType(key, isTupleType(filteredType) || areTupleLikeProperties(getPropertiesOfType(filteredType)) ? filteredType : contextualType);
}

return setCachedType(
key,
discriminateTypeByDiscriminableItems(
filteredType as UnionType,
node.elements.map((element, index) => {
const name = ("" + index) as __String;
return isPossiblyDiscriminantValue(element) && isDiscriminantProperty(filteredType, name) ?
[() => getContextFreeTypeOfExpression(element), name] as const :
undefined;
}).filter(discriminator => !!discriminator),
filter(
map(node.elements, (element, index) => {
const name = ("" + index) as __String;
return isPossiblyDiscriminantValue(element) && isDiscriminantProperty(filteredType, name) ?
[() => getContextFreeTypeOfExpression(element), name] as const :
undefined;
}),
discriminator => !!discriminator,
),
isTypeAssignableTo,
),
);
Expand Down
13 changes: 12 additions & 1 deletion tests/baselines/reference/tupleTypeInference3.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ tupleTypeInference3.ts(16,7): error TS2322: Type '[1, (a: boolean) => void]' is
tupleTypeInference3.ts(30,22): error TS7006: Parameter 'a' implicitly has an 'any' type.
tupleTypeInference3.ts(35,22): error TS7006: Parameter 'a' implicitly has an 'any' type.
tupleTypeInference3.ts(45,24): error TS7006: Parameter 'a' implicitly has an 'any' type.
tupleTypeInference3.ts(58,6): error TS7006: Parameter 'a' implicitly has an 'any' type.


==== tupleTypeInference3.ts (5 errors) ====
==== tupleTypeInference3.ts (6 errors) ====
// Repro from #55632

type InferArg =
Expand Down Expand Up @@ -77,4 +78,14 @@ tupleTypeInference3.ts(45,24): error TS7006: Parameter 'a' implicitly has an 'an
const v112: T11 = [1, (a) => { a === "" }, 0];
const v113: T11 = [1, (a) => { a === 0 }, 0, ""];
const v114: T11 = [1, (a) => { a === "" }, 0, true];

type T12 = { 1: (arg: number) => void } | { 0?: number, 1: (arg: boolean) => void } | { 0: (arg: boolean) => void } | { 0: boolean, 1: (arg: number) => void } | [null, (arg: string) => void];
function f(a: T12) { };
f([null, (a) => { a === "" }]);
f([true, (a) => { a === 0 }]);
f([(a) => { a === true }]);
f([,(a) => { }]); // Error
~
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.


14 changes: 14 additions & 0 deletions tests/baselines/reference/tupleTypeInference3.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ const v111: T11 = [1, (a) => { a === "" }];
const v112: T11 = [1, (a) => { a === "" }, 0];
const v113: T11 = [1, (a) => { a === 0 }, 0, ""];
const v114: T11 = [1, (a) => { a === "" }, 0, true];

type T12 = { 1: (arg: number) => void } | { 0?: number, 1: (arg: boolean) => void } | { 0: (arg: boolean) => void } | { 0: boolean, 1: (arg: number) => void } | [null, (arg: string) => void];
function f(a: T12) { };
f([null, (a) => { a === "" }]);
f([true, (a) => { a === 0 }]);
f([(a) => { a === true }]);
f([,(a) => { }]); // Error



//// [tupleTypeInference3.js]
Expand Down Expand Up @@ -82,3 +90,9 @@ var v111 = [1, function (a) { a === ""; }];
var v112 = [1, function (a) { a === ""; }, 0];
var v113 = [1, function (a) { a === 0; }, 0, ""];
var v114 = [1, function (a) { a === ""; }, 0, true];
function f(a) { }
;
f([null, function (a) { a === ""; }]);
f([true, function (a) { a === 0; }]);
f([function (a) { a === true; }]);
f([, function (a) { }]); // Error
39 changes: 39 additions & 0 deletions tests/baselines/reference/tupleTypeInference3.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,42 @@ const v114: T11 = [1, (a) => { a === "" }, 0, true];
>a : Symbol(a, Decl(tupleTypeInference3.ts, 50, 23))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 50, 23))

type T12 = { 1: (arg: number) => void } | { 0?: number, 1: (arg: boolean) => void } | { 0: (arg: boolean) => void } | { 0: boolean, 1: (arg: number) => void } | [null, (arg: string) => void];
>T12 : Symbol(T12, Decl(tupleTypeInference3.ts, 50, 52))
>1 : Symbol(1, Decl(tupleTypeInference3.ts, 52, 12))
>arg : Symbol(arg, Decl(tupleTypeInference3.ts, 52, 17))
>0 : Symbol(0, Decl(tupleTypeInference3.ts, 52, 43))
>1 : Symbol(1, Decl(tupleTypeInference3.ts, 52, 55))
>arg : Symbol(arg, Decl(tupleTypeInference3.ts, 52, 60))
>0 : Symbol(0, Decl(tupleTypeInference3.ts, 52, 87))
>arg : Symbol(arg, Decl(tupleTypeInference3.ts, 52, 92))
>0 : Symbol(0, Decl(tupleTypeInference3.ts, 52, 119))
>1 : Symbol(1, Decl(tupleTypeInference3.ts, 52, 131))
>arg : Symbol(arg, Decl(tupleTypeInference3.ts, 52, 136))
>arg : Symbol(arg, Decl(tupleTypeInference3.ts, 52, 169))

function f(a: T12) { };
>f : Symbol(f, Decl(tupleTypeInference3.ts, 52, 191))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 53, 11))
>T12 : Symbol(T12, Decl(tupleTypeInference3.ts, 50, 52))

f([null, (a) => { a === "" }]);
>f : Symbol(f, Decl(tupleTypeInference3.ts, 52, 191))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 54, 10))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 54, 10))

f([true, (a) => { a === 0 }]);
>f : Symbol(f, Decl(tupleTypeInference3.ts, 52, 191))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 55, 10))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 55, 10))

f([(a) => { a === true }]);
>f : Symbol(f, Decl(tupleTypeInference3.ts, 52, 191))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 56, 4))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 56, 4))

f([,(a) => { }]); // Error
>f : Symbol(f, Decl(tupleTypeInference3.ts, 52, 191))
>a : Symbol(a, Decl(tupleTypeInference3.ts, 57, 5))


103 changes: 103 additions & 0 deletions tests/baselines/reference/tupleTypeInference3.types
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,106 @@ const v114: T11 = [1, (a) => { a === "" }, 0, true];
>true : true
> : ^^^^

type T12 = { 1: (arg: number) => void } | { 0?: number, 1: (arg: boolean) => void } | { 0: (arg: boolean) => void } | { 0: boolean, 1: (arg: number) => void } | [null, (arg: string) => void];
>T12 : T12
> : ^^^
>1 : (arg: number) => void
> : ^ ^^ ^^^^^
>arg : number
> : ^^^^^^
>0 : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>1 : (arg: boolean) => void
> : ^ ^^ ^^^^^
>arg : boolean
> : ^^^^^^^
>0 : (arg: boolean) => void
> : ^ ^^ ^^^^^
>arg : boolean
> : ^^^^^^^
>0 : boolean
> : ^^^^^^^
>1 : (arg: number) => void
> : ^ ^^ ^^^^^
>arg : number
> : ^^^^^^
>arg : string
> : ^^^^^^

function f(a: T12) { };
>f : (a: T12) => void
> : ^ ^^ ^^^^^^^^^
>a : T12
> : ^^^

f([null, (a) => { a === "" }]);
>f([null, (a) => { a === "" }]) : void
> : ^^^^
>f : (a: T12) => void
> : ^ ^^ ^^^^^^^^^
>[null, (a) => { a === "" }] : [null, (a: string) => void]
> : ^^^^^^^^ ^^^^^^^^^^^^^^^^^^
>(a) => { a === "" } : (a: string) => void
> : ^ ^^^^^^^^^^^^^^^^^
>a : string
> : ^^^^^^
>a === "" : boolean
> : ^^^^^^^
>a : string
> : ^^^^^^
>"" : ""
> : ^^

f([true, (a) => { a === 0 }]);
>f([true, (a) => { a === 0 }]) : void
> : ^^^^
>f : (a: T12) => void
> : ^ ^^ ^^^^^^^^^
>[true, (a) => { a === 0 }] : [true, (a: number) => void]
> : ^^^^^^^^ ^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>(a) => { a === 0 } : (a: number) => void
> : ^ ^^^^^^^^^^^^^^^^^
>a : number
> : ^^^^^^
>a === 0 : boolean
> : ^^^^^^^
>a : number
> : ^^^^^^
>0 : 0
> : ^

f([(a) => { a === true }]);
>f([(a) => { a === true }]) : void
> : ^^^^
>f : (a: T12) => void
> : ^ ^^ ^^^^^^^^^
>[(a) => { a === true }] : [(a: boolean) => void]
> : ^^ ^^^^^^^^^^^^^^^^^^^
>(a) => { a === true } : (a: boolean) => void
> : ^ ^^^^^^^^^^^^^^^^^^
>a : boolean
> : ^^^^^^^
>a === true : boolean
> : ^^^^^^^
>a : boolean
> : ^^^^^^^
>true : true
> : ^^^^

f([,(a) => { }]); // Error
>f([,(a) => { }]) : void
> : ^^^^
>f : (a: T12) => void
> : ^ ^^ ^^^^^^^^^
>[,(a) => { }] : [undefined, (a: any) => void]
> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
> : undefined
> : ^^^^^^^^^
>(a) => { } : (a: any) => void
> : ^ ^^^^^^^^^^^^^^
>a : any
> : ^^^


8 changes: 8 additions & 0 deletions tests/cases/compiler/tupleTypeInference3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@ const v111: T11 = [1, (a) => { a === "" }];
const v112: T11 = [1, (a) => { a === "" }, 0];
const v113: T11 = [1, (a) => { a === 0 }, 0, ""];
const v114: T11 = [1, (a) => { a === "" }, 0, true];

type T12 = { 1: (arg: number) => void } | { 0?: number, 1: (arg: boolean) => void } | { 0: (arg: boolean) => void } | { 0: boolean, 1: (arg: number) => void } | [null, (arg: string) => void];
function f(a: T12) { };
f([null, (a) => { a === "" }]);
f([true, (a) => { a === 0 }]);
f([(a) => { a === true }]);
f([,(a) => { }]); // Error

0 comments on commit 63553c4

Please sign in to comment.