Skip to content

Commit 56f8107

Browse files
authored
Properly check whether union type contains only primitive types (#46645)
* Properly check whether union type contains only primitive types * Add regression test * Remove 'export' modifier from test
1 parent 7f8bf0b commit 56f8107

File tree

6 files changed

+81
-2
lines changed

6 files changed

+81
-2
lines changed

src/compiler/checker.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14153,6 +14153,7 @@ namespace ts {
1415314153
// We ignore 'never' types in unions
1415414154
if (!(flags & TypeFlags.Never)) {
1415514155
includes |= flags & TypeFlags.IncludesMask;
14156+
if (flags & TypeFlags.Instantiable) includes |= TypeFlags.IncludesInstantiable;
1415614157
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
1415714158
if (!strictNullChecks && flags & TypeFlags.Nullable) {
1415814159
if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType;

src/compiler/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -5187,8 +5187,6 @@ namespace ts {
51875187
// 'Narrowable' types are types where narrowing actually narrows.
51885188
// This *should* be every type other than null, undefined, void, and never
51895189
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
5190-
/* @internal */
5191-
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | Instantiable,
51925190
// The following flags are aggregated during union and intersection type construction
51935191
/* @internal */
51945192
IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral,
@@ -5201,6 +5199,10 @@ namespace ts {
52015199
IncludesWildcard = IndexedAccess,
52025200
/* @internal */
52035201
IncludesEmptyObject = Conditional,
5202+
/* @internal */
5203+
IncludesInstantiable = Substitution,
5204+
/* @internal */
5205+
NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | Object | Intersection | IncludesInstantiable,
52045206
}
52055207

52065208
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [primitiveUnionDetection.ts]
2+
// Repro from #46624
3+
4+
type Kind = "one" | "two" | "three";
5+
6+
declare function getInterfaceFromString<T extends Kind>(options?: { type?: T } & { type?: Kind }): T;
7+
8+
const result = getInterfaceFromString({ type: 'two' });
9+
10+
11+
//// [primitiveUnionDetection.js]
12+
"use strict";
13+
// Repro from #46624
14+
var result = getInterfaceFromString({ type: 'two' });
15+
16+
17+
//// [primitiveUnionDetection.d.ts]
18+
declare type Kind = "one" | "two" | "three";
19+
declare function getInterfaceFromString<T extends Kind>(options?: {
20+
type?: T;
21+
} & {
22+
type?: Kind;
23+
}): T;
24+
declare const result: "two";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/primitiveUnionDetection.ts ===
2+
// Repro from #46624
3+
4+
type Kind = "one" | "two" | "three";
5+
>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0))
6+
7+
declare function getInterfaceFromString<T extends Kind>(options?: { type?: T } & { type?: Kind }): T;
8+
>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 36))
9+
>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40))
10+
>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0))
11+
>options : Symbol(options, Decl(primitiveUnionDetection.ts, 4, 56))
12+
>type : Symbol(type, Decl(primitiveUnionDetection.ts, 4, 67))
13+
>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40))
14+
>type : Symbol(type, Decl(primitiveUnionDetection.ts, 4, 82))
15+
>Kind : Symbol(Kind, Decl(primitiveUnionDetection.ts, 0, 0))
16+
>T : Symbol(T, Decl(primitiveUnionDetection.ts, 4, 40))
17+
18+
const result = getInterfaceFromString({ type: 'two' });
19+
>result : Symbol(result, Decl(primitiveUnionDetection.ts, 6, 5))
20+
>getInterfaceFromString : Symbol(getInterfaceFromString, Decl(primitiveUnionDetection.ts, 2, 36))
21+
>type : Symbol(type, Decl(primitiveUnionDetection.ts, 6, 39))
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/compiler/primitiveUnionDetection.ts ===
2+
// Repro from #46624
3+
4+
type Kind = "one" | "two" | "three";
5+
>Kind : Kind
6+
7+
declare function getInterfaceFromString<T extends Kind>(options?: { type?: T } & { type?: Kind }): T;
8+
>getInterfaceFromString : <T extends Kind>(options?: ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined) => T
9+
>options : ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined
10+
>type : T | undefined
11+
>type : Kind | undefined
12+
13+
const result = getInterfaceFromString({ type: 'two' });
14+
>result : "two"
15+
>getInterfaceFromString({ type: 'two' }) : "two"
16+
>getInterfaceFromString : <T extends Kind>(options?: ({ type?: T | undefined; } & { type?: Kind | undefined; }) | undefined) => T
17+
>{ type: 'two' } : { type: "two"; }
18+
>type : "two"
19+
>'two' : "two"
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// Repro from #46624
5+
6+
type Kind = "one" | "two" | "three";
7+
8+
declare function getInterfaceFromString<T extends Kind>(options?: { type?: T } & { type?: Kind }): T;
9+
10+
const result = getInterfaceFromString({ type: 'two' });

0 commit comments

Comments
 (0)