Skip to content

Commit

Permalink
Consistently add undefined/missing to optional tuple element types (#…
Browse files Browse the repository at this point in the history
…50831)

* Consistently add undefined/missing type to optional tuple elements

* Accept new baselines

* Add regression test
  • Loading branch information
ahejlsberg committed Sep 21, 2022
1 parent d90795e commit 01054e0
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 4 deletions.
5 changes: 3 additions & 2 deletions src/compiler/checker.ts
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/mappedTypesArraysTuples.types
Expand Up @@ -7,7 +7,7 @@ type Boxified<T> = { [P in keyof T]: Box<T[P]> };
>Boxified : Boxified<T>

type T00 = Boxified<[number, string?, ...boolean[]]>;
>T00 : [Box<number>, Box<string | undefined>?, ...Box<boolean>[]]
>T00 : [Box<number>, (Box<string | undefined> | undefined)?, ...Box<boolean>[]]

type T01 = Partial<[number, string?, ...boolean[]]>;
>T01 : [(number | undefined)?, (string | undefined)?, ...(boolean | undefined)[]]
Expand Down
33 changes: 33 additions & 0 deletions tests/baselines/reference/optionalTupleElementsAndUndefined.js
@@ -0,0 +1,33 @@
//// [optionalTupleElementsAndUndefined.ts]
// Repro from #50753

type UnNullify<T> = { [K in keyof T]: NonNullable<T[K]> };

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;
@@ -0,0 +1,50 @@
=== tests/cases/compiler/optionalTupleElementsAndUndefined.ts ===
// Repro from #50753

type UnNullify<T> = { [K in keyof T]: NonNullable<T[K]> };
>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))

40 changes: 40 additions & 0 deletions tests/baselines/reference/optionalTupleElementsAndUndefined.types
@@ -0,0 +1,40 @@
=== tests/cases/compiler/optionalTupleElementsAndUndefined.ts ===
// Repro from #50753

type UnNullify<T> = { [K in keyof T]: NonNullable<T[K]> };
>UnNullify : UnNullify<T>

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)?]

2 changes: 1 addition & 1 deletion tests/baselines/reference/variadicTuples1.types
Expand Up @@ -409,7 +409,7 @@ type Arrayify<T> = { [P in keyof T]: T[P][] };
>Arrayify : Arrayify<T>

type TM1<U extends unknown[]> = Arrayify<readonly [string, number?, ...U, ...boolean[]]>; // [string[], (number | undefined)[]?, Arrayify<U>, ...boolean[][]]
>TM1 : readonly [string[], (number | undefined)[]?, ...Arrayify<U>, ...boolean[][]]
>TM1 : readonly [string[], ((number | undefined)[] | undefined)?, ...Arrayify<U>, ...boolean[][]]

type TP1<T extends unknown[]> = Partial<[string, ...T, number]>; // [string?, Partial<T>, number?]
>TP1 : [(string | undefined)?, ...Partial<T>, (number | undefined)?]
Expand Down
20 changes: 20 additions & 0 deletions tests/cases/compiler/optionalTupleElementsAndUndefined.ts
@@ -0,0 +1,20 @@
// @strict: true

// Repro from #50753

type UnNullify<T> = { [K in keyof T]: NonNullable<T[K]> };

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]>;

0 comments on commit 01054e0

Please sign in to comment.