diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ffacdb0abbb2..cfdc767d9cf40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14660,7 +14660,7 @@ namespace ts { if (flags & (ElementFlags.Optional | ElementFlags.Rest)) { lastOptionalOrRestIndex = expandedFlags.length; } - expandedTypes.push(type); + expandedTypes.push(flags & ElementFlags.Optional ? addOptionality(type, /*isProperty*/ true) : type); expandedFlags.push(flags); if (expandedDeclarations && declaration) { expandedDeclarations.push(declaration); @@ -21705,7 +21705,8 @@ namespace ts { function getOptionalType(type: Type, isProperty = false): Type { Debug.assert(strictNullChecks); - return type.flags & TypeFlags.Undefined ? type : getUnionType([type, isProperty ? missingType : undefinedType]); + const missingOrUndefined = isProperty ? missingType : undefinedType; + return type.flags & TypeFlags.Undefined || type.flags & TypeFlags.Union && (type as UnionType).types[0] === missingOrUndefined ? type : getUnionType([type, missingOrUndefined]); } function getGlobalNonNullableTypeInstantiation(type: Type) { diff --git a/tests/baselines/reference/mappedTypesArraysTuples.types b/tests/baselines/reference/mappedTypesArraysTuples.types index dbd83076d3b2a..df45afac118ca 100644 --- a/tests/baselines/reference/mappedTypesArraysTuples.types +++ b/tests/baselines/reference/mappedTypesArraysTuples.types @@ -7,7 +7,7 @@ type Boxified = { [P in keyof T]: Box }; >Boxified : Boxified type T00 = Boxified<[number, string?, ...boolean[]]>; ->T00 : [Box, Box?, ...Box[]] +>T00 : [Box, (Box | undefined)?, ...Box[]] type T01 = Partial<[number, string?, ...boolean[]]>; >T01 : [(number | undefined)?, (string | undefined)?, ...(boolean | undefined)[]] diff --git a/tests/baselines/reference/optionalTupleElementsAndUndefined.js b/tests/baselines/reference/optionalTupleElementsAndUndefined.js new file mode 100644 index 0000000000000..50c0ce32bad30 --- /dev/null +++ b/tests/baselines/reference/optionalTupleElementsAndUndefined.js @@ -0,0 +1,33 @@ +//// [optionalTupleElementsAndUndefined.ts] +// Repro from #50753 + +type UnNullify = { [K in keyof T]: NonNullable }; + +type Foo = UnNullify<[a: 1, b?: 2 | undefined]>; + +type Test = [a: 1, b?: 2] extends Foo ? true : false; // true + +// Types in the following declarations should be identical + +var v: [1, 2?]; +var v: [1, (2 | undefined)?]; +var v: [a: 1, b?: 2]; +var v: [a: 1, b?: 2 | undefined]; +var v: UnNullify<[1, 2?]>; +var v: UnNullify<[1, (2 | undefined)?]>; +var v: UnNullify<[a: 1, b?: 2]>; +var v: UnNullify<[a: 1, b?: 2 | undefined]>; + + +//// [optionalTupleElementsAndUndefined.js] +"use strict"; +// Repro from #50753 +// Types in the following declarations should be identical +var v; +var v; +var v; +var v; +var v; +var v; +var v; +var v; diff --git a/tests/baselines/reference/optionalTupleElementsAndUndefined.symbols b/tests/baselines/reference/optionalTupleElementsAndUndefined.symbols new file mode 100644 index 0000000000000..28646ce51a7f5 --- /dev/null +++ b/tests/baselines/reference/optionalTupleElementsAndUndefined.symbols @@ -0,0 +1,50 @@ +=== tests/cases/compiler/optionalTupleElementsAndUndefined.ts === +// Repro from #50753 + +type UnNullify = { [K in keyof T]: NonNullable }; +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) +>T : Symbol(T, Decl(optionalTupleElementsAndUndefined.ts, 2, 15)) +>K : Symbol(K, Decl(optionalTupleElementsAndUndefined.ts, 2, 23)) +>T : Symbol(T, Decl(optionalTupleElementsAndUndefined.ts, 2, 15)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(optionalTupleElementsAndUndefined.ts, 2, 15)) +>K : Symbol(K, Decl(optionalTupleElementsAndUndefined.ts, 2, 23)) + +type Foo = UnNullify<[a: 1, b?: 2 | undefined]>; +>Foo : Symbol(Foo, Decl(optionalTupleElementsAndUndefined.ts, 2, 58)) +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) + +type Test = [a: 1, b?: 2] extends Foo ? true : false; // true +>Test : Symbol(Test, Decl(optionalTupleElementsAndUndefined.ts, 4, 48)) +>Foo : Symbol(Foo, Decl(optionalTupleElementsAndUndefined.ts, 2, 58)) + +// Types in the following declarations should be identical + +var v: [1, 2?]; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) + +var v: [1, (2 | undefined)?]; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) + +var v: [a: 1, b?: 2]; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) + +var v: [a: 1, b?: 2 | undefined]; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) + +var v: UnNullify<[1, 2?]>; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) + +var v: UnNullify<[1, (2 | undefined)?]>; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) + +var v: UnNullify<[a: 1, b?: 2]>; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) + +var v: UnNullify<[a: 1, b?: 2 | undefined]>; +>v : Symbol(v, Decl(optionalTupleElementsAndUndefined.ts, 10, 3), Decl(optionalTupleElementsAndUndefined.ts, 11, 3), Decl(optionalTupleElementsAndUndefined.ts, 12, 3), Decl(optionalTupleElementsAndUndefined.ts, 13, 3), Decl(optionalTupleElementsAndUndefined.ts, 14, 3) ... and 3 more) +>UnNullify : Symbol(UnNullify, Decl(optionalTupleElementsAndUndefined.ts, 0, 0)) + diff --git a/tests/baselines/reference/optionalTupleElementsAndUndefined.types b/tests/baselines/reference/optionalTupleElementsAndUndefined.types new file mode 100644 index 0000000000000..85ff4920112c2 --- /dev/null +++ b/tests/baselines/reference/optionalTupleElementsAndUndefined.types @@ -0,0 +1,40 @@ +=== tests/cases/compiler/optionalTupleElementsAndUndefined.ts === +// Repro from #50753 + +type UnNullify = { [K in keyof T]: NonNullable }; +>UnNullify : UnNullify + +type Foo = UnNullify<[a: 1, b?: 2 | undefined]>; +>Foo : [a: 1, b?: 2 | undefined] + +type Test = [a: 1, b?: 2] extends Foo ? true : false; // true +>Test : true +>true : true +>false : false + +// Types in the following declarations should be identical + +var v: [1, 2?]; +>v : [1, (2 | undefined)?] + +var v: [1, (2 | undefined)?]; +>v : [1, (2 | undefined)?] + +var v: [a: 1, b?: 2]; +>v : [1, (2 | undefined)?] + +var v: [a: 1, b?: 2 | undefined]; +>v : [1, (2 | undefined)?] + +var v: UnNullify<[1, 2?]>; +>v : [1, (2 | undefined)?] + +var v: UnNullify<[1, (2 | undefined)?]>; +>v : [1, (2 | undefined)?] + +var v: UnNullify<[a: 1, b?: 2]>; +>v : [1, (2 | undefined)?] + +var v: UnNullify<[a: 1, b?: 2 | undefined]>; +>v : [1, (2 | undefined)?] + diff --git a/tests/baselines/reference/variadicTuples1.types b/tests/baselines/reference/variadicTuples1.types index 64a25d510c047..33b04d826e0f7 100644 --- a/tests/baselines/reference/variadicTuples1.types +++ b/tests/baselines/reference/variadicTuples1.types @@ -409,7 +409,7 @@ type Arrayify = { [P in keyof T]: T[P][] }; >Arrayify : Arrayify type TM1 = Arrayify; // [string[], (number | undefined)[]?, Arrayify, ...boolean[][]] ->TM1 : readonly [string[], (number | undefined)[]?, ...Arrayify, ...boolean[][]] +>TM1 : readonly [string[], ((number | undefined)[] | undefined)?, ...Arrayify, ...boolean[][]] type TP1 = Partial<[string, ...T, number]>; // [string?, Partial, number?] >TP1 : [(string | undefined)?, ...Partial, (number | undefined)?] diff --git a/tests/cases/compiler/optionalTupleElementsAndUndefined.ts b/tests/cases/compiler/optionalTupleElementsAndUndefined.ts new file mode 100644 index 0000000000000..1c2a1e9c6a4cc --- /dev/null +++ b/tests/cases/compiler/optionalTupleElementsAndUndefined.ts @@ -0,0 +1,20 @@ +// @strict: true + +// Repro from #50753 + +type UnNullify = { [K in keyof T]: NonNullable }; + +type Foo = UnNullify<[a: 1, b?: 2 | undefined]>; + +type Test = [a: 1, b?: 2] extends Foo ? true : false; // true + +// Types in the following declarations should be identical + +var v: [1, 2?]; +var v: [1, (2 | undefined)?]; +var v: [a: 1, b?: 2]; +var v: [a: 1, b?: 2 | undefined]; +var v: UnNullify<[1, 2?]>; +var v: UnNullify<[1, (2 | undefined)?]>; +var v: UnNullify<[a: 1, b?: 2]>; +var v: UnNullify<[a: 1, b?: 2 | undefined]>;