From c79dff100c6fe8ede9d8fa2a6b4796408363da2b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 2 Nov 2021 11:27:23 -0700 Subject: [PATCH 1/3] Properly check whether union type contains only primitive types --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a8e9eb1ee868..2e051e9774086 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14153,6 +14153,7 @@ namespace ts { // We ignore 'never' types in unions if (!(flags & TypeFlags.Never)) { includes |= flags & TypeFlags.IncludesMask; + if (flags & TypeFlags.Instantiable) includes |= TypeFlags.IncludesInstantiable; if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; if (!strictNullChecks && flags & TypeFlags.Nullable) { if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9a32a79f37f78..92225facae6c6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5187,8 +5187,6 @@ namespace ts { // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, - /* @internal */ - NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | Instantiable, // The following flags are aggregated during union and intersection type construction /* @internal */ IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral, @@ -5201,6 +5199,10 @@ namespace ts { IncludesWildcard = IndexedAccess, /* @internal */ IncludesEmptyObject = Conditional, + /* @internal */ + IncludesInstantiable = Substitution, + /* @internal */ + NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | IncludesInstantiable, } export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; From f9aa40ed9e59ef985406d4fe09e805f69e580197 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 2 Nov 2021 11:27:32 -0700 Subject: [PATCH 2/3] Add regression test --- .../reference/primitiveUnionDetection.js | 19 ++++++++++++++++ .../reference/primitiveUnionDetection.symbols | 22 +++++++++++++++++++ .../reference/primitiveUnionDetection.types | 20 +++++++++++++++++ .../cases/compiler/primitiveUnionDetection.ts | 10 +++++++++ 4 files changed, 71 insertions(+) create mode 100644 tests/baselines/reference/primitiveUnionDetection.js create mode 100644 tests/baselines/reference/primitiveUnionDetection.symbols create mode 100644 tests/baselines/reference/primitiveUnionDetection.types create mode 100644 tests/cases/compiler/primitiveUnionDetection.ts diff --git a/tests/baselines/reference/primitiveUnionDetection.js b/tests/baselines/reference/primitiveUnionDetection.js new file mode 100644 index 0000000000000..b35dae3f8a41b --- /dev/null +++ b/tests/baselines/reference/primitiveUnionDetection.js @@ -0,0 +1,19 @@ +//// [primitiveUnionDetection.ts] +// Repro from #46624 + +export type Kind = "one" | "two" | "three"; + +declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; + +const result = getInterfaceFromString({ type: 'two' }); + + +//// [primitiveUnionDetection.js] +"use strict"; +// Repro from #46624 +exports.__esModule = true; +var result = getInterfaceFromString({ type: 'two' }); + + +//// [primitiveUnionDetection.d.ts] +export declare type Kind = "one" | "two" | "three"; diff --git a/tests/baselines/reference/primitiveUnionDetection.symbols b/tests/baselines/reference/primitiveUnionDetection.symbols new file mode 100644 index 0000000000000..90c7b819e33c2 --- /dev/null +++ b/tests/baselines/reference/primitiveUnionDetection.symbols @@ -0,0 +1,22 @@ +=== tests/cases/compiler/primitiveUnionDetection.ts === +// Repro from #46624 + +export type Kind = "one" | "two" | "three"; +>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0)) + +declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; +>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 43)) +>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40)) +>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0)) +>options : Symbol(options, Decl(primitiveUnionDetection.ts, 4, 56)) +>type : Symbol(type, Decl(primitiveUnionDetection.ts, 4, 67)) +>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40)) +>type : Symbol(type, Decl(primitiveUnionDetection.ts, 4, 82)) +>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0)) +>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40)) + +const result = getInterfaceFromString({ type: 'two' }); +>result : Symbol(result, Decl(primitiveUnionDetection.ts, 6, 5)) +>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 43)) +>type : Symbol(type, Decl(primitiveUnionDetection.ts, 6, 39)) + diff --git a/tests/baselines/reference/primitiveUnionDetection.types b/tests/baselines/reference/primitiveUnionDetection.types new file mode 100644 index 0000000000000..fa4f4265f2ded --- /dev/null +++ b/tests/baselines/reference/primitiveUnionDetection.types @@ -0,0 +1,20 @@ +=== tests/cases/compiler/primitiveUnionDetection.ts === +// Repro from #46624 + +export type Kind = "one" | "two" | "three"; +>Kind : Kind + +declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; +>getInterfaceFromString : (options?: ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined) => T +>options : ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined +>type : T | undefined +>type : Kind | undefined + +const result = getInterfaceFromString({ type: 'two' }); +>result : "two" +>getInterfaceFromString({ type: 'two' }) : "two" +>getInterfaceFromString : (options?: ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined) => T +>{ type: 'two' } : { type: "two"; } +>type : "two" +>'two' : "two" + diff --git a/tests/cases/compiler/primitiveUnionDetection.ts b/tests/cases/compiler/primitiveUnionDetection.ts new file mode 100644 index 0000000000000..a01809c797b33 --- /dev/null +++ b/tests/cases/compiler/primitiveUnionDetection.ts @@ -0,0 +1,10 @@ +// @strict: true +// @declaration: true + +// Repro from #46624 + +export type Kind = "one" | "two" | "three"; + +declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; + +const result = getInterfaceFromString({ type: 'two' }); From 5010346da54b0c6b66304c0316f0d0a506de3069 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 2 Nov 2021 11:33:00 -0700 Subject: [PATCH 3/3] Remove 'export' modifier from test --- tests/baselines/reference/primitiveUnionDetection.js | 11 ++++++++--- .../reference/primitiveUnionDetection.symbols | 6 +++--- .../baselines/reference/primitiveUnionDetection.types | 2 +- tests/cases/compiler/primitiveUnionDetection.ts | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/primitiveUnionDetection.js b/tests/baselines/reference/primitiveUnionDetection.js index b35dae3f8a41b..7d12cd85f9201 100644 --- a/tests/baselines/reference/primitiveUnionDetection.js +++ b/tests/baselines/reference/primitiveUnionDetection.js @@ -1,7 +1,7 @@ //// [primitiveUnionDetection.ts] // Repro from #46624 -export type Kind = "one" | "two" | "three"; +type Kind = "one" | "two" | "three"; declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; @@ -11,9 +11,14 @@ const result = getInterfaceFromString({ type: 'two' }); //// [primitiveUnionDetection.js] "use strict"; // Repro from #46624 -exports.__esModule = true; var result = getInterfaceFromString({ type: 'two' }); //// [primitiveUnionDetection.d.ts] -export declare type Kind = "one" | "two" | "three"; +declare type Kind = "one" | "two" | "three"; +declare function getInterfaceFromString(options?: { + type?: T; +} & { + type?: Kind; +}): T; +declare const result: "two"; diff --git a/tests/baselines/reference/primitiveUnionDetection.symbols b/tests/baselines/reference/primitiveUnionDetection.symbols index 90c7b819e33c2..d5fa7d4447193 100644 --- a/tests/baselines/reference/primitiveUnionDetection.symbols +++ b/tests/baselines/reference/primitiveUnionDetection.symbols @@ -1,11 +1,11 @@ === tests/cases/compiler/primitiveUnionDetection.ts === // Repro from #46624 -export type Kind = "one" | "two" | "three"; +type Kind = "one" | "two" | "three"; >Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0)) declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; ->getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 43)) +>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 36)) >T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40)) >Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0)) >options : Symbol(options, Decl(primitiveUnionDetection.ts, 4, 56)) @@ -17,6 +17,6 @@ declare function getInterfaceFromString(options?: { type?: T } & const result = getInterfaceFromString({ type: 'two' }); >result : Symbol(result, Decl(primitiveUnionDetection.ts, 6, 5)) ->getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 43)) +>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 36)) >type : Symbol(type, Decl(primitiveUnionDetection.ts, 6, 39)) diff --git a/tests/baselines/reference/primitiveUnionDetection.types b/tests/baselines/reference/primitiveUnionDetection.types index fa4f4265f2ded..ac2243d80d4aa 100644 --- a/tests/baselines/reference/primitiveUnionDetection.types +++ b/tests/baselines/reference/primitiveUnionDetection.types @@ -1,7 +1,7 @@ === tests/cases/compiler/primitiveUnionDetection.ts === // Repro from #46624 -export type Kind = "one" | "two" | "three"; +type Kind = "one" | "two" | "three"; >Kind : Kind declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T; diff --git a/tests/cases/compiler/primitiveUnionDetection.ts b/tests/cases/compiler/primitiveUnionDetection.ts index a01809c797b33..4d6cc5c661aea 100644 --- a/tests/cases/compiler/primitiveUnionDetection.ts +++ b/tests/cases/compiler/primitiveUnionDetection.ts @@ -3,7 +3,7 @@ // Repro from #46624 -export type Kind = "one" | "two" | "three"; +type Kind = "one" | "two" | "three"; declare function getInterfaceFromString(options?: { type?: T } & { type?: Kind }): T;