Port "Improve type discrimination algorithm" from tsgo (#61828)

This commit is contained in:
Jake Bailey 2025-06-09 16:20:15 -07:00 committed by GitHub
parent 0dfd0c2143
commit 12e09f44f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 239 additions and 29 deletions

View File

@ -24802,11 +24802,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
for (let i = 0; i < types.length; i++) {
if (include[i]) {
const targetType = getTypeOfPropertyOrIndexSignatureOfType(types[i], propertyName);
if (targetType && someType(getDiscriminatingType(), t => !!related(t, targetType))) {
matched = true;
}
else {
include[i] = Ternary.Maybe;
if (targetType) {
if (someType(getDiscriminatingType(), t => !!related(t, targetType))) {
matched = true;
}
else {
include[i] = Ternary.Maybe;
}
}
}
}

View File

@ -370,7 +370,7 @@ function createImportAdderWorker(sourceFile: SourceFile | FutureSourceFile, prog
);
let fix: FixAddNewImport | ImportFixWithModuleSpecifier;
if (existingFix && importKind !== ImportKind.Namespace) {
if (existingFix && importKind !== ImportKind.Namespace && existingFix.kind !== ImportFixKind.UseNamespace && existingFix.kind !== ImportFixKind.JsdocTypeImport) {
fix = {
...existingFix,
addAsTypeOnly,

View File

@ -1,22 +0,0 @@
discriminateWithMissingProperty.ts(12,5): error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array<ArrayBuffer>; }' is not assignable to parameter of type 'Arg'.
Types of property 'data' are incompatible.
Type 'Uint8Array<ArrayBuffer>' is not assignable to type 'number'.
==== discriminateWithMissingProperty.ts (1 errors) ====
type Arg = {
mode: "numeric",
data: number,
} | {
mode: "alphabetic",
data: string,
} | {
data: string | Uint8Array;
}
declare function foo(arg: Arg): void;
foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array<ArrayBuffer>; }' is not assignable to parameter of type 'Arg'.
!!! error TS2345: Types of property 'data' are incompatible.
!!! error TS2345: Type 'Uint8Array<ArrayBuffer>' is not assignable to type 'number'.

View File

@ -0,0 +1,28 @@
missingDiscriminants.ts(17,23): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
missingDiscriminants.ts(18,34): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
==== missingDiscriminants.ts (2 errors) ====
// https://github.com/microsoft/typescript-go/issues/1020
type Thing =
| { str: "a", num: 0 }
| { str: "b" }
| { num: 1 }
const thing1: Thing = { str: "a", num: 0 }
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
type Item =
| { kind: "a", subkind: 0, value: string }
| { kind: "a", subkind: 1, value: number }
| { kind: "b" }
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
~~~~~~~
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
~~~~~~~
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.

View File

@ -0,0 +1,64 @@
//// [tests/cases/compiler/missingDiscriminants.ts] ////
=== missingDiscriminants.ts ===
// https://github.com/microsoft/typescript-go/issues/1020
type Thing =
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
| { str: "a", num: 0 }
>str : Symbol(str, Decl(missingDiscriminants.ts, 3, 5))
>num : Symbol(num, Decl(missingDiscriminants.ts, 3, 15))
| { str: "b" }
>str : Symbol(str, Decl(missingDiscriminants.ts, 4, 5))
| { num: 1 }
>num : Symbol(num, Decl(missingDiscriminants.ts, 5, 5))
const thing1: Thing = { str: "a", num: 0 }
>thing1 : Symbol(thing1, Decl(missingDiscriminants.ts, 7, 5))
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
>str : Symbol(str, Decl(missingDiscriminants.ts, 7, 23))
>num : Symbol(num, Decl(missingDiscriminants.ts, 7, 33))
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
>thing2 : Symbol(thing2, Decl(missingDiscriminants.ts, 8, 5))
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
>str : Symbol(str, Decl(missingDiscriminants.ts, 8, 23))
>num : Symbol(num, Decl(missingDiscriminants.ts, 8, 33))
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
>thing3 : Symbol(thing3, Decl(missingDiscriminants.ts, 9, 5))
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
>num : Symbol(num, Decl(missingDiscriminants.ts, 9, 23))
>str : Symbol(str, Decl(missingDiscriminants.ts, 9, 31))
type Item =
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
| { kind: "a", subkind: 0, value: string }
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 12, 5))
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 12, 16))
>value : Symbol(value, Decl(missingDiscriminants.ts, 12, 28))
| { kind: "a", subkind: 1, value: number }
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 13, 5))
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 13, 16))
>value : Symbol(value, Decl(missingDiscriminants.ts, 13, 28))
| { kind: "b" }
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 14, 5))
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
>item1 : Symbol(item1, Decl(missingDiscriminants.ts, 16, 5))
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 16, 21))
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 16, 33))
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
>item2 : Symbol(item2, Decl(missingDiscriminants.ts, 17, 5))
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 17, 21))
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 17, 32))

View File

@ -0,0 +1,117 @@
//// [tests/cases/compiler/missingDiscriminants.ts] ////
=== missingDiscriminants.ts ===
// https://github.com/microsoft/typescript-go/issues/1020
type Thing =
>Thing : Thing
> : ^^^^^
| { str: "a", num: 0 }
>str : "a"
> : ^^^
>num : 0
> : ^
| { str: "b" }
>str : "b"
> : ^^^
| { num: 1 }
>num : 1
> : ^
const thing1: Thing = { str: "a", num: 0 }
>thing1 : Thing
> : ^^^^^
>{ str: "a", num: 0 } : { str: "a"; num: 0; }
> : ^^^^^^^^^^^^^^^^^^^^^
>str : "a"
> : ^^^
>"a" : "a"
> : ^^^
>num : 0
> : ^
>0 : 0
> : ^
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
>thing2 : Thing
> : ^^^^^
>{ str: "b", num: 1 } : { str: "b"; num: 1; }
> : ^^^^^^^^^^^^^^^^^^^^^
>str : "b"
> : ^^^
>"b" : "b"
> : ^^^
>num : 1
> : ^
>1 : 1
> : ^
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
>thing3 : Thing
> : ^^^^^
>{ num: 1, str: "b" } : { num: 1; str: "b"; }
> : ^^^^^^^^^^^^^^^^^^^^^
>num : 1
> : ^
>1 : 1
> : ^
>str : "b"
> : ^^^
>"b" : "b"
> : ^^^
type Item =
>Item : Item
> : ^^^^
| { kind: "a", subkind: 0, value: string }
>kind : "a"
> : ^^^
>subkind : 0
> : ^
>value : string
> : ^^^^^^
| { kind: "a", subkind: 1, value: number }
>kind : "a"
> : ^^^
>subkind : 1
> : ^
>value : number
> : ^^^^^^
| { kind: "b" }
>kind : "b"
> : ^^^
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
>item1 : Item
> : ^^^^
>{ subkind: 1, kind: "b" } : { subkind: number; kind: "b"; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>subkind : number
> : ^^^^^^
>1 : 1
> : ^
>kind : "b"
> : ^^^
>"b" : "b"
> : ^^^
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
>item2 : Item
> : ^^^^
>{ kind: "b", subkind: 1 } : { kind: "b"; subkind: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>kind : "b"
> : ^^^
>"b" : "b"
> : ^^^
>subkind : number
> : ^^^^^^
>1 : 1
> : ^

View File

@ -1,7 +1,7 @@
//// [tests/cases/compiler/promiseType.ts] ////
=== Performance Stats ===
Instantiation count: 2,500
Instantiation count: 1,000
=== promiseType.ts ===
declare var p: Promise<boolean>;

View File

@ -0,0 +1,21 @@
// @strict: true
// @noEmit: true
// https://github.com/microsoft/typescript-go/issues/1020
type Thing =
| { str: "a", num: 0 }
| { str: "b" }
| { num: 1 }
const thing1: Thing = { str: "a", num: 0 }
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
type Item =
| { kind: "a", subkind: 0, value: string }
| { kind: "a", subkind: 1, value: number }
| { kind: "b" }
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property