From ce23b8d18fe9c026c1247217b38275071a81d011 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 5 May 2022 08:31:36 -0700 Subject: [PATCH 01/30] Improve reduction of intersection types --- src/compiler/checker.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5a1f6f440fe7e..92dc554fe68e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12058,7 +12058,7 @@ namespace ts { } } } - else if (t.flags & TypeFlags.DisjointDomains) { + else if (t.flags & TypeFlags.DisjointDomains || isEmptyAnonymousObjectType(t)) { hasDisjointDomainType = true; } } @@ -12069,7 +12069,7 @@ namespace ts { // We add any types belong to one of the disjoint domains because they might cause the final // intersection operation to reduce the union constraints. for (const t of types) { - if (t.flags & TypeFlags.DisjointDomains) { + if (t.flags & TypeFlags.DisjointDomains || isEmptyAnonymousObjectType(t)) { constraints = append(constraints, t); } } @@ -14826,7 +14826,7 @@ namespace ts { return includes; } - function removeRedundantPrimitiveTypes(types: Type[], includes: TypeFlags) { + function removeRedundantSupertypes(types: Type[], includes: TypeFlags) { let i = types.length; while (i > 0) { i--; @@ -14835,7 +14835,9 @@ namespace ts { t.flags & TypeFlags.String && includes & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral || t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || - t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol; + t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || + t.flags & TypeFlags.NonPrimitive && includes & TypeFlags.Object || + isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable; if (remove) { orderedRemoveItemAt(types, i); } @@ -14996,11 +14998,10 @@ namespace ts { if (includes & TypeFlags.String && includes & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral || includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || - includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol) { - removeRedundantPrimitiveTypes(typeSet, includes); - } - if (includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.Object) { - orderedRemoveItemAt(typeSet, findIndex(typeSet, isEmptyAnonymousObjectType)); + includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || + includes & TypeFlags.NonPrimitive && includes & TypeFlags.Object || + includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) { + removeRedundantSupertypes(typeSet, includes); } if (includes & TypeFlags.IncludesMissingType) { typeSet[typeSet.indexOf(undefinedType)] = missingType; From ffe147ef61eff3b0a13e6e65f999531d0773f688 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 5 May 2022 08:35:22 -0700 Subject: [PATCH 02/30] Accept new baselines --- .../findAllRefsRootSymbols.baseline.jsonc | 88 +------------------ .../intersectionOfUnionNarrowing.types | 12 +-- .../intersectionsAndEmptyObjects.types | 4 +- .../nonPrimitiveUnionIntersection.errors.txt | 14 ++- .../nonPrimitiveUnionIntersection.types | 6 +- .../objectLiteralExcessProperties.errors.txt | 8 +- .../objectLiteralExcessProperties.types | 2 +- .../reference/recursiveTypeRelations.types | 6 +- .../templateLiteralIntersection.types | 2 +- .../reference/templateLiteralTypes2.types | 6 +- 10 files changed, 33 insertions(+), 115 deletions(-) diff --git a/tests/baselines/reference/findAllRefsRootSymbols.baseline.jsonc b/tests/baselines/reference/findAllRefsRootSymbols.baseline.jsonc index e262c15548b19..f8cf052e206f3 100644 --- a/tests/baselines/reference/findAllRefsRootSymbols.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsRootSymbols.baseline.jsonc @@ -89,7 +89,7 @@ "containerName": "", "fileName": "/tests/cases/fourslash/findAllRefsRootSymbols.ts", "kind": "property", - "name": "(property) x: {} & string", + "name": "(property) x: string", "textSpan": { "start": 14, "length": 1 @@ -123,26 +123,6 @@ "text": " ", "kind": "space" }, - { - "text": "{", - "kind": "punctuation" - }, - { - "text": "}", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "&", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, { "text": "string", "kind": "keyword" @@ -258,7 +238,7 @@ "containerName": "", "fileName": "/tests/cases/fourslash/findAllRefsRootSymbols.ts", "kind": "property", - "name": "(property) x: {} & string", + "name": "(property) x: string", "textSpan": { "start": 14, "length": 1 @@ -292,26 +272,6 @@ "text": " ", "kind": "space" }, - { - "text": "{", - "kind": "punctuation" - }, - { - "text": "}", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "&", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, { "text": "string", "kind": "keyword" @@ -415,7 +375,7 @@ "containerName": "", "fileName": "/tests/cases/fourslash/findAllRefsRootSymbols.ts", "kind": "property", - "name": "(property) x: {} & string", + "name": "(property) x: string", "textSpan": { "start": 14, "length": 1 @@ -449,26 +409,6 @@ "text": " ", "kind": "space" }, - { - "text": "{", - "kind": "punctuation" - }, - { - "text": "}", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "&", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, { "text": "string", "kind": "keyword" @@ -725,7 +665,7 @@ "containerName": "", "fileName": "/tests/cases/fourslash/findAllRefsRootSymbols.ts", "kind": "property", - "name": "(property) x: {} & string", + "name": "(property) x: string", "textSpan": { "start": 14, "length": 1 @@ -759,26 +699,6 @@ "text": " ", "kind": "space" }, - { - "text": "{", - "kind": "punctuation" - }, - { - "text": "}", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "&", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, { "text": "string", "kind": "keyword" diff --git a/tests/baselines/reference/intersectionOfUnionNarrowing.types b/tests/baselines/reference/intersectionOfUnionNarrowing.types index 5a9072bfa60bc..e2b526101be48 100644 --- a/tests/baselines/reference/intersectionOfUnionNarrowing.types +++ b/tests/baselines/reference/intersectionOfUnionNarrowing.types @@ -20,25 +20,25 @@ declare const q: X & AorB; if (q.a !== undefined) { >q.a !== undefined : boolean ->q.a : ({ aProp: string; } & object) | undefined +>q.a : { aProp: string; } | undefined >q : X & AorB ->a : ({ aProp: string; } & object) | undefined +>a : { aProp: string; } | undefined >undefined : undefined q.a.aProp; >q.a.aProp : string ->q.a : { aProp: string; } & object +>q.a : { aProp: string; } >q : X & { a: object; b: undefined; } ->a : { aProp: string; } & object +>a : { aProp: string; } >aProp : string } else { // q.b is previously incorrectly inferred as potentially undefined q.b.bProp; >q.b.bProp : string ->q.b : { bProp: string; } & object +>q.b : { bProp: string; } >q : X & { a: undefined; b: object; } ->b : { bProp: string; } & object +>b : { bProp: string; } >bProp : string } diff --git a/tests/baselines/reference/intersectionsAndEmptyObjects.types b/tests/baselines/reference/intersectionsAndEmptyObjects.types index 5459f614a89ed..6972e06169dcb 100644 --- a/tests/baselines/reference/intersectionsAndEmptyObjects.types +++ b/tests/baselines/reference/intersectionsAndEmptyObjects.types @@ -26,10 +26,10 @@ let x04: A & B & C; >x04 : A & B let x05: string & C; ->x05 : string & C +>x05 : string let x06: C & string; ->x06 : C & string +>x06 : string let x07: C; >x07 : C diff --git a/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt b/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt index d4c970c5064f0..5050b21ecfd67 100644 --- a/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt +++ b/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt @@ -1,9 +1,8 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(1,5): error TS2322: Type 'string' is not assignable to type 'never'. -tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(3,5): error TS2322: Type 'number' is not assignable to type 'object & {}'. - Type 'number' is not assignable to type 'object'. +tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(3,5): error TS2322: Type 'number' is not assignable to type 'object'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(4,1): error TS2322: Type 'string' is not assignable to type 'never'. -tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38): error TS2322: Type '{ bar: string; }' is not assignable to type 'object & { err: string; }'. - Object literal may only specify known properties, and 'bar' does not exist in type 'object & { err: string; }'. +tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38): error TS2322: Type '{ bar: string; }' is not assignable to type '{ err: string; }'. + Object literal may only specify known properties, and 'bar' does not exist in type '{ err: string; }'. ==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts (4 errors) ==== @@ -13,8 +12,7 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38 var b: object | string = ""; // ok var c: object & {} = 123; // error ~ -!!! error TS2322: Type 'number' is not assignable to type 'object & {}'. -!!! error TS2322: Type 'number' is not assignable to type 'object'. +!!! error TS2322: Type 'number' is not assignable to type 'object'. a = b; // error ~ !!! error TS2322: Type 'string' is not assignable to type 'never'. @@ -23,6 +21,6 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38 const foo: object & {} = {bar: 'bar'}; // ok const bar: object & {err: string} = {bar: 'bar'}; // error ~~~~~~~~~~ -!!! error TS2322: Type '{ bar: string; }' is not assignable to type 'object & { err: string; }'. -!!! error TS2322: Object literal may only specify known properties, and 'bar' does not exist in type 'object & { err: string; }'. +!!! error TS2322: Type '{ bar: string; }' is not assignable to type '{ err: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'bar' does not exist in type '{ err: string; }'. \ No newline at end of file diff --git a/tests/baselines/reference/nonPrimitiveUnionIntersection.types b/tests/baselines/reference/nonPrimitiveUnionIntersection.types index ef7e2f8928b93..aa1ed8e0458d3 100644 --- a/tests/baselines/reference/nonPrimitiveUnionIntersection.types +++ b/tests/baselines/reference/nonPrimitiveUnionIntersection.types @@ -8,7 +8,7 @@ var b: object | string = ""; // ok >"" : "" var c: object & {} = 123; // error ->c : object & {} +>c : object >123 : 123 a = b; // error @@ -22,13 +22,13 @@ b = a; // ok >a : never const foo: object & {} = {bar: 'bar'}; // ok ->foo : object & {} +>foo : object >{bar: 'bar'} : { bar: string; } >bar : string >'bar' : "bar" const bar: object & {err: string} = {bar: 'bar'}; // error ->bar : object & { err: string; } +>bar : { err: string; } >err : string >{bar: 'bar'} : { bar: string; } >bar : string diff --git a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt index 245293d9a7cb6..c1653ac4da5fd 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt +++ b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt @@ -28,8 +28,8 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(43,43): error TS2322: Type Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. tests/cases/compiler/objectLiteralExcessProperties.ts(45,76): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type '(T & { prop: boolean; }) | { name: string; }'. Object literal may only specify known properties, and 'prop' does not exist in type '{ name: string; }'. -tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. - Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. +tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type '{ z: string; }' is not assignable to type '{ x: string; }'. + Object literal may only specify known properties, and 'z' does not exist in type '{ x: string; }'. ==== tests/cases/compiler/objectLiteralExcessProperties.ts (16 errors) ==== @@ -129,7 +129,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type // The 'object' type has no effect on intersections const obj6: object & { x: string } = { z: 'abc' } ~~~~~~~~ -!!! error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. -!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. +!!! error TS2322: Type '{ z: string; }' is not assignable to type '{ x: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type '{ x: string; }'. } \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralExcessProperties.types b/tests/baselines/reference/objectLiteralExcessProperties.types index cd3be132fe2d3..1e527efa6201b 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.types +++ b/tests/baselines/reference/objectLiteralExcessProperties.types @@ -151,7 +151,7 @@ function test() { // The 'object' type has no effect on intersections const obj6: object & { x: string } = { z: 'abc' } ->obj6 : object & { x: string; } +>obj6 : { x: string; } >x : string >{ z: 'abc' } : { z: string; } >z : string diff --git a/tests/baselines/reference/recursiveTypeRelations.types b/tests/baselines/reference/recursiveTypeRelations.types index d632e0c097336..7e318eebbbb50 100644 --- a/tests/baselines/reference/recursiveTypeRelations.types +++ b/tests/baselines/reference/recursiveTypeRelations.types @@ -24,7 +24,7 @@ type ClassNameMap = { [K in keyof S]?: boolean } >ClassNameMap : ClassNameMap type ClassNameObjectMap = object & ClassNameMap; ->ClassNameObjectMap : ClassNameObjectMap +>ClassNameObjectMap : ClassNameMap type ClassNameArg = ClassName | ClassNameObjectMap; >ClassNameArg : ClassNameArg @@ -75,7 +75,7 @@ export function css(styles: S, ...classNam >Object.keys : (o: object) => string[] >Object : ObjectConstructor >keys : (o: object) => string[] ->arg : ClassNameObjectMap +>arg : ClassNameMap >reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } >(obj: ClassNameObject, key: keyof S) => { const exportedClassName = styles[key]; obj[exportedClassName] = (arg as ClassNameMap)[key]; return obj; } : (obj: ClassNameObject, key: keyof S) => ClassNameObject >obj : ClassNameObject @@ -95,7 +95,7 @@ export function css(styles: S, ...classNam >(arg as ClassNameMap)[key] : boolean >(arg as ClassNameMap) : ClassNameMap >arg as ClassNameMap : ClassNameMap ->arg : ClassNameObjectMap +>arg : ClassNameMap >key : keyof S return obj; diff --git a/tests/baselines/reference/templateLiteralIntersection.types b/tests/baselines/reference/templateLiteralIntersection.types index b305f6677740f..0c716d40a731d 100644 --- a/tests/baselines/reference/templateLiteralIntersection.types +++ b/tests/baselines/reference/templateLiteralIntersection.types @@ -54,7 +54,7 @@ type E = `${A & {}}`; >E : "a" type MixE = E & {} ->MixE : "a" & {} +>MixE : "a" type OriginE = `${MixE}` >OriginE : "a" diff --git a/tests/baselines/reference/templateLiteralTypes2.types b/tests/baselines/reference/templateLiteralTypes2.types index e771c63c5357c..cb8443cd9ca5c 100644 --- a/tests/baselines/reference/templateLiteralTypes2.types +++ b/tests/baselines/reference/templateLiteralTypes2.types @@ -402,13 +402,13 @@ const interpolatedStyle = { rotate: 12 }; function C2(transform: "-moz-initial" | (string & {})) { return 12; } >C2 : (transform: "-moz-initial" | (string & {})) => number ->transform : (string & {}) | "-moz-initial" +>transform : string >12 : 12 C2(`rotate(${interpolatedStyle.rotate}dig)`); >C2(`rotate(${interpolatedStyle.rotate}dig)`) : number ->C2 : (transform: (string & {}) | "-moz-initial") => number ->`rotate(${interpolatedStyle.rotate}dig)` : `rotate(${number}dig)` +>C2 : (transform: string) => number +>`rotate(${interpolatedStyle.rotate}dig)` : string >interpolatedStyle.rotate : number >interpolatedStyle : { rotate: number; } >rotate : number From 29edef115cc190ab5df15e11fb50a52e0a8a8e7c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 15 May 2022 12:16:46 -0700 Subject: [PATCH 03/30] Improve CFA for truthy, equality, and typeof checks --- src/compiler/checker.ts | 79 +++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dfa82d6d931c1..51c04ae1ce894 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -848,6 +848,8 @@ namespace ts { emptyTypeLiteralSymbol.members = createSymbolTable(); const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray); + const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray)]) : unknownType; + const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType; emptyGenericType.instantiations = new Map(); @@ -14835,6 +14837,7 @@ namespace ts { t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral || t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || + t.flags & TypeFlags.Void && includes & TypeFlags.Undefined || t.flags & TypeFlags.NonPrimitive && includes & TypeFlags.Object || isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable; if (remove) { @@ -14998,6 +15001,7 @@ namespace ts { includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral || includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || + includes & TypeFlags.Void && includes & TypeFlags.Undefined || includes & TypeFlags.NonPrimitive && includes & TypeFlags.Object || includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) { removeRedundantSupertypes(typeSet, includes); @@ -18183,7 +18187,7 @@ namespace ts { // Since unions and intersections may reduce to `never`, we exclude them here. if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true; if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true; - if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true; + if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source))) return true; if (relation === assignableRelation || relation === comparableRelation) { if (s & TypeFlags.Any) return true; // Type number or any numeric literal type is assignable to any numeric enum type or any @@ -23505,6 +23509,24 @@ namespace ts { return filterType(type, t => (getTypeFacts(t) & include) !== 0); } + function getIntersectionWithFacts(type: Type, facts: TypeFacts) { + const reduced = getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts); + if (strictNullChecks) { + switch (facts) { + case TypeFacts.NEUndefined: + const emptyOrNull = maybeTypeOfKind(reduced, TypeFlags.Null) ? emptyObjectType : getUnionType([emptyObjectType, nullType]); + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull ? emptyOrNull : emptyObjectType]): t); + case TypeFacts.NENull: + const emptyOrUndefined = maybeTypeOfKind(reduced, TypeFlags.Undefined) ? emptyObjectType : getUnionType([emptyObjectType, undefinedType]); + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined ? emptyOrUndefined : emptyObjectType]): t); + case TypeFacts.NEUndefinedOrNull: + case TypeFacts.Truthy: + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getIntersectionType([t, emptyObjectType]): t); + } + } + return reduced; + } + function getTypeWithDefault(type: Type, defaultExpression: Expression) { return defaultExpression ? getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) : @@ -24637,6 +24659,9 @@ namespace ts { return getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))); } const result = getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction); + if (result === unknownUnionType) { + return unknownType; + } if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arraysEqual((result as UnionType).types, (declaredType as UnionType).types)) { return declaredType; } @@ -24744,8 +24769,7 @@ namespace ts { function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { if (isMatchingReference(reference, expr)) { - return type.flags & TypeFlags.Unknown && assumeTrue ? nonNullUnknownType : - getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); + return getIntersectionWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) { type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); @@ -24905,9 +24929,6 @@ namespace ts { assumeTrue = !assumeTrue; } const valueType = getTypeOfExpression(value); - if (assumeTrue && (type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken) && (valueType.flags & TypeFlags.Null)) { - return getUnionType([nullType, undefinedType]); - } if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) { return valueType; @@ -24927,7 +24948,7 @@ namespace ts { valueType.flags & TypeFlags.Null ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; - return type.flags & TypeFlags.Unknown && facts & (TypeFacts.NENull | TypeFacts.NEUndefinedOrNull) ? nonNullUnknownType : getTypeWithFacts(type, facts); + return getIntersectionWithFacts(type, facts); } if (assumeTrue) { const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ? @@ -24956,17 +24977,11 @@ namespace ts { if (type.flags & TypeFlags.Any && literal.text === "function") { return type; } - if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") { - // The non-null unknown type is used to track whether a previous narrowing operation has removed the null type - // from the unknown type. For example, the expression `x && typeof x === 'object'` first narrows x to the non-null - // unknown type, and then narrows that to the non-primitive type. - return type === nonNullUnknownType ? nonPrimitiveType : getUnionType([nonPrimitiveType, nullType]); - } const facts = assumeTrue ? typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; const impliedType = getImpliedTypeFromTypeofGuard(type, literal.text); - return getTypeWithFacts(assumeTrue && impliedType ? mapType(type, narrowUnionMemberByTypeof(impliedType)) : type, facts); + return getTypeWithFacts(assumeTrue && impliedType ? narrowTypeByImpliedType(type, impliedType) : type, facts); } function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) { @@ -25019,10 +25034,10 @@ namespace ts { function getImpliedTypeFromTypeofGuard(type: Type, text: string) { switch (text) { + case "object": + return type.flags & TypeFlags.Any ? type : getUnionType([nullType, nonPrimitiveType]); case "function": return type.flags & TypeFlags.Any ? type : globalFunctionType; - case "object": - return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type; default: return typeofTypesByName.get(text); } @@ -25034,22 +25049,24 @@ namespace ts { // the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not // the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to // filtering by type-facts. - function narrowUnionMemberByTypeof(candidate: Type) { - return (type: Type) => { - if (isTypeSubtypeOf(type, candidate)) { - return type; - } - if (isTypeSubtypeOf(candidate, type)) { - return candidate; + function narrowTypeByImpliedType(type: Type, candidate: Type) { + if (type.flags & TypeFlags.AnyOrUnknown) { + return candidate; + } + return mapType(type, t => { + if (isTypeRelatedTo(t, candidate, strictSubtypeRelation)) { + return t; } - if (type.flags & TypeFlags.Instantiable) { - const constraint = getBaseConstraintOfType(type) || anyType; - if (isTypeSubtypeOf(candidate, constraint)) { - return getIntersectionType([type, candidate]); + return mapType(candidate, c => { + if (!areTypesComparable(t, c)) { + return neverType; } - } - return type; - }; + if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { + return isTypeSubtypeOf(c, t) ? c : neverType; + } + return getIntersectionType([t, c]); + }); + }); } function narrowBySwitchOnTypeOf(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type { @@ -25109,7 +25126,7 @@ namespace ts { because it is caught in the first clause. */ const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text) || type)), switchFacts); - return getTypeWithFacts(mapType(type, narrowUnionMemberByTypeof(impliedType)), switchFacts); + return getTypeWithFacts(narrowTypeByImpliedType(type, impliedType), switchFacts); } function isMatchingConstructorReference(expr: Expression) { From 39326d7180f16e89953fe66cdf5f284522b452ab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 15 May 2022 12:19:05 -0700 Subject: [PATCH 04/30] Accept new baselines --- .../reference/assertionTypePredicates1.types | 2 +- .../reference/controlFlowGenericTypes.types | 16 ++-- .../reference/controlFlowTruthiness.types | 2 +- .../reference/controlFlowTypeofObject.types | 14 ++-- ...nDoesNotOperateOnPrimitiveTypes.errors.txt | 5 +- .../inDoesNotOperateOnPrimitiveTypes.types | 2 +- .../reference/mappedTypes4.errors.txt | 77 +++++++++++++++++++ tests/baselines/reference/mappedTypes4.types | 12 +-- .../reference/narrowByEquality.types | 4 +- .../reference/narrowingByTypeofInSwitch.types | 18 ++--- .../reference/narrowingTruthyObject.types | 12 +-- .../reference/narrowingTypeofFunction.types | 2 +- .../reference/strictOptionalProperties1.types | 2 +- .../reference/typeGuardTypeOfUndefined.types | 6 +- .../reference/typeVariableTypeGuards.types | 4 +- tests/baselines/reference/unknownType2.types | 2 +- .../reference/voidUndefinedReduction.types | 2 +- 17 files changed, 128 insertions(+), 54 deletions(-) create mode 100644 tests/baselines/reference/mappedTypes4.errors.txt diff --git a/tests/baselines/reference/assertionTypePredicates1.types b/tests/baselines/reference/assertionTypePredicates1.types index 064cf6066f4dd..c8f4c2e063ae4 100644 --- a/tests/baselines/reference/assertionTypePredicates1.types +++ b/tests/baselines/reference/assertionTypePredicates1.types @@ -150,7 +150,7 @@ function f01(x: unknown) { >undefined : undefined >typeof x === "string" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} | null >"string" : "string" x; // string | undefined diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index fc502a7dede82..7916b0f60e034 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -10,7 +10,7 @@ function f1(x: T, y: { a: T }, z: [T]): string { >x : T x; ->x : T +>x : T & {} x.length; >x.length : number @@ -44,13 +44,13 @@ function f1(x: T, y: { a: T }, z: [T]): string { z[0].length; >z[0].length : number ->z[0] : string +>z[0] : T & {} >z : [T] >0 : 0 >length : number return z[0]; ->z[0] : string +>z[0] : T & {} >z : [T] >0 : 0 } @@ -67,7 +67,7 @@ function f2(x: Extract | null): string { >x : Extract | null x; ->x : Extract +>x : Extract & {} x.length; >x.length : number @@ -404,11 +404,11 @@ function fx1(obj: T, key: K) { >key : K const x2 = obj && obj[key]; ->x2 : T[K] ->obj && obj[key] : T[K] ->obj : T ->obj[key] : T[K] +>x2 : (T & {})[K] +>obj && obj[key] : (T & {})[K] >obj : T +>obj[key] : (T & {})[K] +>obj : T & {} >key : K } diff --git a/tests/baselines/reference/controlFlowTruthiness.types b/tests/baselines/reference/controlFlowTruthiness.types index 17f497267ee6e..ba1486785bdb0 100644 --- a/tests/baselines/reference/controlFlowTruthiness.types +++ b/tests/baselines/reference/controlFlowTruthiness.types @@ -181,7 +181,7 @@ function f8(x: T) { >x : T x; // {} ->x : T +>x : T & {} } else { x; // {} diff --git a/tests/baselines/reference/controlFlowTypeofObject.types b/tests/baselines/reference/controlFlowTypeofObject.types index 68d99e6f0c266..fbd81cc8024b5 100644 --- a/tests/baselines/reference/controlFlowTypeofObject.types +++ b/tests/baselines/reference/controlFlowTypeofObject.types @@ -16,7 +16,7 @@ function f1(x: unknown) { if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" obj(x); @@ -40,7 +40,7 @@ function f2(x: unknown) { if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} | undefined >'object' : "object" obj(x); @@ -64,7 +64,7 @@ function f3(x: unknown) { if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" obj(x); @@ -88,7 +88,7 @@ function f4(x: unknown) { if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" obj(x); @@ -126,7 +126,7 @@ function f5(x: unknown) { if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} | undefined >'object' : "object" obj(x); @@ -150,12 +150,12 @@ function f6(x: unknown) { } else { x; ->x : unknown +>x : {} | undefined if (typeof x === 'object') { >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} | undefined >'object' : "object" obj(x); diff --git a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt index 90652969ed18a..049b46621e8ac 100644 --- a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt +++ b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt @@ -3,12 +3,11 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(23,12): error TS2361: T tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(27,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(34,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(53,14): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(55,18): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(60,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -==== tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts (8 errors) ==== +==== tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts (7 errors) ==== const validHasKey = ( thing: T, key: string, @@ -74,8 +73,6 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. if (typeof p === "object") { "key" in p; - ~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. } } diff --git a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.types b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.types index 9175fd9022fc8..f56bbf9395d40 100644 --- a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.types +++ b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.types @@ -146,7 +146,7 @@ function union5(p: T | U) "key" in p; >"key" in p : boolean >"key" : "key" ->p : T | U +>p : (T & object) | (U & object) } } diff --git a/tests/baselines/reference/mappedTypes4.errors.txt b/tests/baselines/reference/mappedTypes4.errors.txt new file mode 100644 index 0000000000000..e8b8032c24cd9 --- /dev/null +++ b/tests/baselines/reference/mappedTypes4.errors.txt @@ -0,0 +1,77 @@ +tests/cases/conformance/types/mapped/mappedTypes4.ts(11,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '(T & null) | (T & object)'. + + +==== tests/cases/conformance/types/mapped/mappedTypes4.ts (1 errors) ==== + type Box = { + }; + + type Boxified = { + [P in keyof T]: Box; + }; + + function boxify(obj: T): Boxified { + if (typeof obj === "object") { + let result = {} as Boxified; + for (let k in obj) { + ~~~ +!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '(T & null) | (T & object)'. + result[k] = { value: obj[k] }; + } + return result; + } + return obj; + } + + type A = { a: string }; + type B = { b: string }; + type C = { c: string }; + + function f1(x: A | B | C | undefined) { + return boxify(x); + } + + type T00 = Partial; + type T01 = Readonly; + type T02 = Boxified + type T03 = Readonly; + type T04 = Boxified; + type T05 = Partial<"hello" | "world" | 42>; + + type BoxifiedWithSentinel = { + [P in keyof T]: Box | U; + } + + type T10 = BoxifiedWithSentinel; + type T11 = BoxifiedWithSentinel; + type T12 = BoxifiedWithSentinel; + + type DeepReadonly = { + readonly [P in keyof T]: DeepReadonly; + }; + + type Foo = { + x: number; + y: { a: string, b: number }; + z: boolean; + }; + + type DeepReadonlyFoo = { + readonly x: number; + readonly y: { readonly a: string, readonly b: number }; + readonly z: boolean; + }; + + var x1: DeepReadonly; + var x1: DeepReadonlyFoo; + + // Repro from #13232 + + type Z = { a: number }; + type Clone = { + [P in keyof (T & {})]: T[P]; + }; + type M = Clone; // M should be { a: number } + + var z1: Z; + var z1: Clone; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypes4.types b/tests/baselines/reference/mappedTypes4.types index 85e714a4f0043..5c09db18ec538 100644 --- a/tests/baselines/reference/mappedTypes4.types +++ b/tests/baselines/reference/mappedTypes4.types @@ -27,17 +27,17 @@ function boxify(obj: T): Boxified { for (let k in obj) { >k : Extract ->obj : T +>obj : (T & null) | (T & object) result[k] = { value: obj[k] }; ->result[k] = { value: obj[k] } : { value: T[Extract]; } +>result[k] = { value: obj[k] } : { value: ((T & null) | (T & object))[Extract]; } >result[k] : Boxified[Extract] >result : Boxified >k : Extract ->{ value: obj[k] } : { value: T[Extract]; } ->value : T[Extract] ->obj[k] : T[Extract] ->obj : T +>{ value: obj[k] } : { value: ((T & null) | (T & object))[Extract]; } +>value : ((T & null) | (T & object))[Extract] +>obj[k] : ((T & null) | (T & object))[Extract] +>obj : (T & null) | (T & object) >k : Extract } return result; diff --git a/tests/baselines/reference/narrowByEquality.types b/tests/baselines/reference/narrowByEquality.types index 4f5c8a5195840..f34a49558b2b2 100644 --- a/tests/baselines/reference/narrowByEquality.types +++ b/tests/baselines/reference/narrowByEquality.types @@ -144,7 +144,7 @@ if (xUnknown == null) { } else { xUnknown ->xUnknown : unknown +>xUnknown : {} } if (xUnknown != null) { @@ -153,7 +153,7 @@ if (xUnknown != null) { >null : null xUnknown; ->xUnknown : unknown +>xUnknown : {} } else { xUnknown; diff --git a/tests/baselines/reference/narrowingByTypeofInSwitch.types b/tests/baselines/reference/narrowingByTypeofInSwitch.types index 96897cd71de20..8d06e9243c6cc 100644 --- a/tests/baselines/reference/narrowingByTypeofInSwitch.types +++ b/tests/baselines/reference/narrowingByTypeofInSwitch.types @@ -552,14 +552,14 @@ function multipleGenericFuse(xy: X | case 'function': return [xy, 1]; >'function' : "function" ->[xy, 1] : [X, 1] ->xy : X +>[xy, 1] : [X & Function, 1] +>xy : X & Function >1 : 1 case 'object': return [xy, 'two']; >'object' : "object" ->[xy, 'two'] : [Y, string] ->xy : Y +>[xy, 'two'] : [Y & object, string] +>xy : Y & object >'two' : "two" case 'number': return [xy] @@ -843,7 +843,7 @@ function narrowingNarrows(x: {} | undefined) { case 'object': const _: {} = x; return; >'object' : "object" >_ : {} ->x : {} +>x : object case 'string': assertString(x); return; >'string' : "string" @@ -1092,14 +1092,14 @@ function multipleGenericFuseWithBoth case `function`: return [xy, 1]; >`function` : "function" ->[xy, 1] : [X, 1] ->xy : X +>[xy, 1] : [X & Function, 1] +>xy : X & Function >1 : 1 case 'object': return [xy, 'two']; >'object' : "object" ->[xy, 'two'] : [Y, string] ->xy : Y +>[xy, 'two'] : [Y & object, string] +>xy : Y & object >'two' : "two" case `number`: return [xy] diff --git a/tests/baselines/reference/narrowingTruthyObject.types b/tests/baselines/reference/narrowingTruthyObject.types index c562f3da2d3ed..463faf955560a 100644 --- a/tests/baselines/reference/narrowingTruthyObject.types +++ b/tests/baselines/reference/narrowingTruthyObject.types @@ -35,7 +35,7 @@ function foo(x: unknown, b: boolean) { >x : unknown >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" x.toString(); @@ -51,7 +51,7 @@ function foo(x: unknown, b: boolean) { >x : unknown >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" x.toString(); @@ -67,7 +67,7 @@ function foo(x: unknown, b: boolean) { >b : boolean >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" x.toString(); @@ -85,7 +85,7 @@ function foo(x: unknown, b: boolean) { >b : true >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" x.toString(); @@ -107,7 +107,7 @@ function foo(x: unknown, b: boolean) { >b : true >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" x.toString(); @@ -130,7 +130,7 @@ function f1(x: unknown): any { >x : unknown >typeof x === 'object' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} >'object' : "object" >x.hasOwnProperty('x') : boolean >x.hasOwnProperty : (v: PropertyKey) => boolean diff --git a/tests/baselines/reference/narrowingTypeofFunction.types b/tests/baselines/reference/narrowingTypeofFunction.types index 6290fe95c3782..19b11f2b2feea 100644 --- a/tests/baselines/reference/narrowingTypeofFunction.types +++ b/tests/baselines/reference/narrowingTypeofFunction.types @@ -35,7 +35,7 @@ function f2(x: (T & F) | T & string) { >"function" : "function" x; ->x : (T & F) | (T & string) +>x : T & F } else { x; diff --git a/tests/baselines/reference/strictOptionalProperties1.types b/tests/baselines/reference/strictOptionalProperties1.types index ba14aedfd1846..ae5ccf531ca05 100644 --- a/tests/baselines/reference/strictOptionalProperties1.types +++ b/tests/baselines/reference/strictOptionalProperties1.types @@ -653,7 +653,7 @@ function expectNotUndefined(value: Undefinable): T { >'value is undefined' : "value is undefined" } return value; ->value : T +>value : T & ({} | null) } interface Bar { diff --git a/tests/baselines/reference/typeGuardTypeOfUndefined.types b/tests/baselines/reference/typeGuardTypeOfUndefined.types index 79f87e101caa8..ffc82e57e6693 100644 --- a/tests/baselines/reference/typeGuardTypeOfUndefined.types +++ b/tests/baselines/reference/typeGuardTypeOfUndefined.types @@ -242,7 +242,7 @@ function test9(a: boolean | number) { } else { a; ->a : undefined +>a : never } } @@ -259,7 +259,7 @@ function test10(a: boolean | number) { if (typeof a === "boolean") { >typeof a === "boolean" : boolean >typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->a : undefined +>a : never >"boolean" : "boolean" a; @@ -267,7 +267,7 @@ function test10(a: boolean | number) { } else { a; ->a : undefined +>a : never } } else { diff --git a/tests/baselines/reference/typeVariableTypeGuards.types b/tests/baselines/reference/typeVariableTypeGuards.types index 9f6f45935a7a9..a1c9e1c39431e 100644 --- a/tests/baselines/reference/typeVariableTypeGuards.types +++ b/tests/baselines/reference/typeVariableTypeGuards.types @@ -193,8 +193,8 @@ function f5(obj: T | undefined, key: K) { >obj : T | undefined obj[key]; ->obj[key] : T[K] ->obj : T +>obj[key] : (T & {})[K] +>obj : T & {} >key : K } } diff --git a/tests/baselines/reference/unknownType2.types b/tests/baselines/reference/unknownType2.types index cbb9269c76304..634546bb5ffba 100644 --- a/tests/baselines/reference/unknownType2.types +++ b/tests/baselines/reference/unknownType2.types @@ -640,7 +640,7 @@ function f2(x: unknown): string | undefined { >undefined : undefined >typeof x !== 'string' : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : unknown +>x : {} | null >'string' : "string" throw new Error(); diff --git a/tests/baselines/reference/voidUndefinedReduction.types b/tests/baselines/reference/voidUndefinedReduction.types index a11667bf47655..8f8f54222d67b 100644 --- a/tests/baselines/reference/voidUndefinedReduction.types +++ b/tests/baselines/reference/voidUndefinedReduction.types @@ -12,7 +12,7 @@ function isDefined(value: T | undefined | null | void): value is T { >value : void | T | null | undefined >undefined : undefined >value !== null : boolean ->value : T | null +>value : (T & {}) | null >null : null } From d0b7ec8eaa72b9e80b462f621d5ee87455459954 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 15 May 2022 13:12:03 -0700 Subject: [PATCH 05/30] Remove special case for Function type --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 51c04ae1ce894..1a9d5d3d0a190 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25061,7 +25061,7 @@ namespace ts { if (!areTypesComparable(t, c)) { return neverType; } - if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { + if ((c.flags & TypeFlags.Primitive) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { return isTypeSubtypeOf(c, t) ? c : neverType; } return getIntersectionType([t, c]); From 95c3c565602bcc3984c190a5863444c2927e925a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 15 May 2022 16:43:13 -0700 Subject: [PATCH 06/30] Don't reduce intersections of form {...} & object --- src/compiler/checker.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab10bb6e8534e..580baf20d42bf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14838,7 +14838,6 @@ namespace ts { t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || t.flags & TypeFlags.Void && includes & TypeFlags.Undefined || - t.flags & TypeFlags.NonPrimitive && includes & TypeFlags.Object || isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable; if (remove) { orderedRemoveItemAt(types, i); @@ -15002,7 +15001,6 @@ namespace ts { includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || includes & TypeFlags.Void && includes & TypeFlags.Undefined || - includes & TypeFlags.NonPrimitive && includes & TypeFlags.Object || includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) { removeRedundantSupertypes(typeSet, includes); } @@ -25061,9 +25059,12 @@ namespace ts { if (!areTypesComparable(t, c)) { return neverType; } - if ((c.flags & TypeFlags.Primitive) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { + if (c.flags & TypeFlags.Primitive && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { return isTypeSubtypeOf(c, t) ? c : neverType; } + if (c === globalFunctionType && t.flags & TypeFlags.NonPrimitive) { + return c; + } return getIntersectionType([t, c]); }); }); From 986963cde0223e4e4d48fb3afce704a1e6ef7906 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 15 May 2022 16:43:34 -0700 Subject: [PATCH 07/30] Accept new baselines --- .../reference/intersectionOfUnionNarrowing.types | 12 ++++++------ .../nonPrimitiveUnionIntersection.errors.txt | 8 ++++---- .../reference/nonPrimitiveUnionIntersection.types | 2 +- .../objectLiteralExcessProperties.errors.txt | 8 ++++---- .../reference/objectLiteralExcessProperties.types | 2 +- .../baselines/reference/recursiveTypeRelations.types | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/baselines/reference/intersectionOfUnionNarrowing.types b/tests/baselines/reference/intersectionOfUnionNarrowing.types index e2b526101be48..5a9072bfa60bc 100644 --- a/tests/baselines/reference/intersectionOfUnionNarrowing.types +++ b/tests/baselines/reference/intersectionOfUnionNarrowing.types @@ -20,25 +20,25 @@ declare const q: X & AorB; if (q.a !== undefined) { >q.a !== undefined : boolean ->q.a : { aProp: string; } | undefined +>q.a : ({ aProp: string; } & object) | undefined >q : X & AorB ->a : { aProp: string; } | undefined +>a : ({ aProp: string; } & object) | undefined >undefined : undefined q.a.aProp; >q.a.aProp : string ->q.a : { aProp: string; } +>q.a : { aProp: string; } & object >q : X & { a: object; b: undefined; } ->a : { aProp: string; } +>a : { aProp: string; } & object >aProp : string } else { // q.b is previously incorrectly inferred as potentially undefined q.b.bProp; >q.b.bProp : string ->q.b : { bProp: string; } +>q.b : { bProp: string; } & object >q : X & { a: undefined; b: object; } ->b : { bProp: string; } +>b : { bProp: string; } & object >bProp : string } diff --git a/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt b/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt index 5050b21ecfd67..84c3f768436b9 100644 --- a/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt +++ b/tests/baselines/reference/nonPrimitiveUnionIntersection.errors.txt @@ -1,8 +1,8 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(1,5): error TS2322: Type 'string' is not assignable to type 'never'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(3,5): error TS2322: Type 'number' is not assignable to type 'object'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(4,1): error TS2322: Type 'string' is not assignable to type 'never'. -tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38): error TS2322: Type '{ bar: string; }' is not assignable to type '{ err: string; }'. - Object literal may only specify known properties, and 'bar' does not exist in type '{ err: string; }'. +tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38): error TS2322: Type '{ bar: string; }' is not assignable to type 'object & { err: string; }'. + Object literal may only specify known properties, and 'bar' does not exist in type 'object & { err: string; }'. ==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts (4 errors) ==== @@ -21,6 +21,6 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveUnionIntersection.ts(8,38 const foo: object & {} = {bar: 'bar'}; // ok const bar: object & {err: string} = {bar: 'bar'}; // error ~~~~~~~~~~ -!!! error TS2322: Type '{ bar: string; }' is not assignable to type '{ err: string; }'. -!!! error TS2322: Object literal may only specify known properties, and 'bar' does not exist in type '{ err: string; }'. +!!! error TS2322: Type '{ bar: string; }' is not assignable to type 'object & { err: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'bar' does not exist in type 'object & { err: string; }'. \ No newline at end of file diff --git a/tests/baselines/reference/nonPrimitiveUnionIntersection.types b/tests/baselines/reference/nonPrimitiveUnionIntersection.types index aa1ed8e0458d3..0a4a870a15b8d 100644 --- a/tests/baselines/reference/nonPrimitiveUnionIntersection.types +++ b/tests/baselines/reference/nonPrimitiveUnionIntersection.types @@ -28,7 +28,7 @@ const foo: object & {} = {bar: 'bar'}; // ok >'bar' : "bar" const bar: object & {err: string} = {bar: 'bar'}; // error ->bar : { err: string; } +>bar : object & { err: string; } >err : string >{bar: 'bar'} : { bar: string; } >bar : string diff --git a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt index c1653ac4da5fd..245293d9a7cb6 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt +++ b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt @@ -28,8 +28,8 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(43,43): error TS2322: Type Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. tests/cases/compiler/objectLiteralExcessProperties.ts(45,76): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type '(T & { prop: boolean; }) | { name: string; }'. Object literal may only specify known properties, and 'prop' does not exist in type '{ name: string; }'. -tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type '{ z: string; }' is not assignable to type '{ x: string; }'. - Object literal may only specify known properties, and 'z' does not exist in type '{ x: string; }'. +tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. + Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. ==== tests/cases/compiler/objectLiteralExcessProperties.ts (16 errors) ==== @@ -129,7 +129,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type // The 'object' type has no effect on intersections const obj6: object & { x: string } = { z: 'abc' } ~~~~~~~~ -!!! error TS2322: Type '{ z: string; }' is not assignable to type '{ x: string; }'. -!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type '{ x: string; }'. +!!! error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. } \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralExcessProperties.types b/tests/baselines/reference/objectLiteralExcessProperties.types index 1e527efa6201b..cd3be132fe2d3 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.types +++ b/tests/baselines/reference/objectLiteralExcessProperties.types @@ -151,7 +151,7 @@ function test() { // The 'object' type has no effect on intersections const obj6: object & { x: string } = { z: 'abc' } ->obj6 : { x: string; } +>obj6 : object & { x: string; } >x : string >{ z: 'abc' } : { z: string; } >z : string diff --git a/tests/baselines/reference/recursiveTypeRelations.types b/tests/baselines/reference/recursiveTypeRelations.types index 7e318eebbbb50..d632e0c097336 100644 --- a/tests/baselines/reference/recursiveTypeRelations.types +++ b/tests/baselines/reference/recursiveTypeRelations.types @@ -24,7 +24,7 @@ type ClassNameMap = { [K in keyof S]?: boolean } >ClassNameMap : ClassNameMap type ClassNameObjectMap = object & ClassNameMap; ->ClassNameObjectMap : ClassNameMap +>ClassNameObjectMap : ClassNameObjectMap type ClassNameArg = ClassName | ClassNameObjectMap; >ClassNameArg : ClassNameArg @@ -75,7 +75,7 @@ export function css(styles: S, ...classNam >Object.keys : (o: object) => string[] >Object : ObjectConstructor >keys : (o: object) => string[] ->arg : ClassNameMap +>arg : ClassNameObjectMap >reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } >(obj: ClassNameObject, key: keyof S) => { const exportedClassName = styles[key]; obj[exportedClassName] = (arg as ClassNameMap)[key]; return obj; } : (obj: ClassNameObject, key: keyof S) => ClassNameObject >obj : ClassNameObject @@ -95,7 +95,7 @@ export function css(styles: S, ...classNam >(arg as ClassNameMap)[key] : boolean >(arg as ClassNameMap) : ClassNameMap >arg as ClassNameMap : ClassNameMap ->arg : ClassNameMap +>arg : ClassNameObjectMap >key : keyof S return obj; From c40018150d5a3896c8816db654fc14e5ed802ab0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 16 May 2022 14:16:47 -0700 Subject: [PATCH 08/30] Anything is assignable to unknown-like union --- src/compiler/checker.ts | 16 +++++++++++++++- src/compiler/types.ts | 5 +++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 580baf20d42bf..a6adfbab3f019 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18118,6 +18118,18 @@ namespace ts { type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral && getMembersOfSymbol(type.symbol).size === 0)); } + function isUnknownLikeUnionType(type: Type) { + if (strictNullChecks && type.flags & TypeFlags.Union) { + if (!((type as UnionType).objectFlags & ObjectFlags.IsUnknownLikeUnionComputed)) { + const types = (type as UnionType).types; + (type as UnionType).objectFlags |= ObjectFlags.IsUnknownLikeUnionComputed | (types.length >= 3 && types[0].flags & TypeFlags.Undefined && + types[1].flags & TypeFlags.Null && some(types, isEmptyAnonymousObjectType) ? ObjectFlags.IsUnknownLikeUnion : 0); + } + return !!((type as UnionType).objectFlags & ObjectFlags.IsUnknownLikeUnion); + } + return false; + } + function isStringIndexSignatureOnlyType(type: Type): boolean { return type.flags & TypeFlags.Object && !isGenericMappedType(type) && getPropertiesOfType(type).length === 0 && getIndexInfosOfType(type).length === 1 && !!getIndexInfoOfType(type, stringType) || type.flags & TypeFlags.UnionOrIntersection && every((type as UnionOrIntersectionType).types, isStringIndexSignatureOnlyType) || @@ -18193,6 +18205,8 @@ namespace ts { // bit-flag enum types sometimes look like literal enum types with numeric literal values. if (s & (TypeFlags.Number | TypeFlags.NumberLiteral) && !(s & TypeFlags.EnumLiteral) && ( t & TypeFlags.Enum || relation === assignableRelation && t & TypeFlags.NumberLiteral && t & TypeFlags.EnumLiteral)) return true; + // Anything is assignable to a union containing undefined, null, and {} + if (isUnknownLikeUnionType(target)) return true; } return false; } @@ -25059,7 +25073,7 @@ namespace ts { if (!areTypesComparable(t, c)) { return neverType; } - if (c.flags & TypeFlags.Primitive && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { + if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { return isTypeSubtypeOf(c, t) ? c : neverType; } if (c === globalFunctionType && t.flags & TypeFlags.NonPrimitive) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index db071689cd303..86fda375a897b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5397,6 +5397,11 @@ namespace ts { // Flags that require TypeFlags.Union /* @internal */ ContainsIntersections = 1 << 24, // Union contains intersections + /* @internal */ + IsUnknownLikeUnionComputed = 1 << 25, // IsUnknownLikeUnion flag has been computed + /* @internal */ + IsUnknownLikeUnion = 1 << 26, // Union of null, undefined, and empty object type + /* @internal */ // Flags that require TypeFlags.Intersection /* @internal */ From f1ebcdd78cc90c612db285c0b9119397b524c93b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 16 May 2022 14:16:56 -0700 Subject: [PATCH 09/30] Accept new baselines --- tests/baselines/reference/unknownType1.errors.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/baselines/reference/unknownType1.errors.txt b/tests/baselines/reference/unknownType1.errors.txt index 342d0fbb706f1..9aa098a781c53 100644 --- a/tests/baselines/reference/unknownType1.errors.txt +++ b/tests/baselines/reference/unknownType1.errors.txt @@ -14,7 +14,6 @@ tests/cases/conformance/types/unknown/unknownType1.ts(110,9): error TS2322: Type tests/cases/conformance/types/unknown/unknownType1.ts(111,9): error TS2322: Type 'unknown' is not assignable to type 'string'. tests/cases/conformance/types/unknown/unknownType1.ts(112,9): error TS2322: Type 'unknown' is not assignable to type 'string[]'. tests/cases/conformance/types/unknown/unknownType1.ts(113,9): error TS2322: Type 'unknown' is not assignable to type '{}'. -tests/cases/conformance/types/unknown/unknownType1.ts(114,9): error TS2322: Type 'unknown' is not assignable to type '{} | null | undefined'. tests/cases/conformance/types/unknown/unknownType1.ts(120,9): error TS2322: Type 'T' is not assignable to type 'object'. tests/cases/conformance/types/unknown/unknownType1.ts(128,5): error TS2322: Type 'number[]' is not assignable to type '{ [x: string]: unknown; }'. Index signature for type 'string' is missing in type 'number[]'. @@ -28,7 +27,7 @@ tests/cases/conformance/types/unknown/unknownType1.ts(171,9): error TS2322: Type tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type 'T' is not assignable to type '{}'. -==== tests/cases/conformance/types/unknown/unknownType1.ts (27 errors) ==== +==== tests/cases/conformance/types/unknown/unknownType1.ts (26 errors) ==== // In an intersection everything absorbs unknown type T00 = unknown & null; // null @@ -175,8 +174,6 @@ tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type ~~ !!! error TS2322: Type 'unknown' is not assignable to type '{}'. let v7: {} | null | undefined = x; // Error - ~~ -!!! error TS2322: Type 'unknown' is not assignable to type '{} | null | undefined'. } // Type parameter 'T extends unknown' not related to object From 73c91fd71192e83062e6ebe92666131248aeff7b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 16 May 2022 16:45:02 -0700 Subject: [PATCH 10/30] Tweak subtype check --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a6adfbab3f019..8d24ff9a24e24 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25073,10 +25073,10 @@ namespace ts { if (!areTypesComparable(t, c)) { return neverType; } - if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { + if (c.flags & TypeFlags.Primitive && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) { return isTypeSubtypeOf(c, t) ? c : neverType; } - if (c === globalFunctionType && t.flags & TypeFlags.NonPrimitive) { + if (c === globalFunctionType && isTypeSubtypeOf(c, t)) { return c; } return getIntersectionType([t, c]); From 722c462b3c51f3661cc8ca83b50a36fa4814c52c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 17 May 2022 15:15:24 -0700 Subject: [PATCH 11/30] Recombine unknown type from unknown-like union in more cases --- src/compiler/checker.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d24ff9a24e24..8bdbdf7f2ddd8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23522,15 +23522,13 @@ namespace ts { } function getIntersectionWithFacts(type: Type, facts: TypeFacts) { - const reduced = getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts); + const reduced = recombineUnknownType(getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts)); if (strictNullChecks) { switch (facts) { case TypeFacts.NEUndefined: - const emptyOrNull = maybeTypeOfKind(reduced, TypeFlags.Null) ? emptyObjectType : getUnionType([emptyObjectType, nullType]); - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull ? emptyOrNull : emptyObjectType]): t); + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull && !maybeTypeOfKind(reduced, TypeFlags.Null) ? getUnionType([emptyObjectType, nullType]) : emptyObjectType]): t); case TypeFacts.NENull: - const emptyOrUndefined = maybeTypeOfKind(reduced, TypeFlags.Undefined) ? emptyObjectType : getUnionType([emptyObjectType, undefinedType]); - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined ? emptyOrUndefined : emptyObjectType]): t); + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined && !maybeTypeOfKind(reduced, TypeFlags.Undefined) ? getUnionType([emptyObjectType, undefinedType]) : emptyObjectType]): t); case TypeFacts.NEUndefinedOrNull: case TypeFacts.Truthy: return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getIntersectionType([t, emptyObjectType]): t); @@ -23539,6 +23537,10 @@ namespace ts { return reduced; } + function recombineUnknownType(type: Type) { + return type === unknownUnionType ? unknownType : type; + } + function getTypeWithDefault(type: Type, defaultExpression: Expression) { return defaultExpression ? getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) : @@ -24670,10 +24672,7 @@ namespace ts { if (isEvolvingArrayTypeList(types)) { return getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))); } - const result = getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction); - if (result === unknownUnionType) { - return unknownType; - } + const result = recombineUnknownType(getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction)); if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arraysEqual((result as UnionType).types, (declaredType as UnionType).types)) { return declaredType; } From 5e1111a19394baf004fa7f60f124cedfb993ace6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 18 May 2022 15:59:34 -0700 Subject: [PATCH 12/30] Display union origin only if it is shorter than union itself --- src/compiler/checker.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8bdbdf7f2ddd8..a1379f1b9ab5c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5041,7 +5041,8 @@ namespace ts { (type === markerSubType ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?"; return factory.createTypeReferenceNode(factory.createIdentifier(name), /*typeArguments*/ undefined); } - if (type.flags & TypeFlags.Union && (type as UnionType).origin) { + // Switch to the union origin only if it has fewer constituents than the union itself. + if (type.flags & TypeFlags.Union && (type as UnionType).origin && getConstituentCount(type) > getConstituentCount((type as UnionType).origin!)) { type = (type as UnionType).origin!; } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { @@ -15088,6 +15089,11 @@ namespace ts { return intersections; } + function getConstituentCount(type: Type): number { + return !(type.flags & TypeFlags.UnionOrIntersection) ? 1 : reduceLeft((type as UnionOrIntersectionType).types, + (n, t) => n + (t.aliasSymbol ? 1 : getConstituentCount(t.flags & TypeFlags.Union && (t as UnionType).origin || t)), 0); + } + function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { From 5c579a4df447a0c0efc8780b5db23df9274db9ee Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 18 May 2022 16:00:41 -0700 Subject: [PATCH 13/30] Accept new baselines --- tests/baselines/reference/spreadObjectOrFalsy.js | 2 +- .../baselines/reference/spreadObjectOrFalsy.types | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/spreadObjectOrFalsy.js b/tests/baselines/reference/spreadObjectOrFalsy.js index 9aae56dc08562..c7fcb4fb13b36 100644 --- a/tests/baselines/reference/spreadObjectOrFalsy.js +++ b/tests/baselines/reference/spreadObjectOrFalsy.js @@ -111,7 +111,7 @@ declare function f5(a: S | T): S | T; declare function f6(a: T): T; declare function g1(a: A): (T | undefined) & T; +}>(a: A): T | (undefined & T); interface DatafulFoo { data: T; } diff --git a/tests/baselines/reference/spreadObjectOrFalsy.types b/tests/baselines/reference/spreadObjectOrFalsy.types index 1b9d54512e9f7..71e0d59f24a30 100644 --- a/tests/baselines/reference/spreadObjectOrFalsy.types +++ b/tests/baselines/reference/spreadObjectOrFalsy.types @@ -56,19 +56,19 @@ function f6(a: T) { // Repro from #46976 function g1(a: A) { ->g1 : (a: A) => (T | undefined) & T ->z : (T | undefined) & T +>g1 : (a: A) => T | (undefined & T) +>z : T | (undefined & T) >a : A const { z } = a; ->z : (T | undefined) & T +>z : T | (undefined & T) >a : A return { ->{ ...z } : (T | undefined) & T +>{ ...z } : T | (undefined & T) ...z ->z : (T | undefined) & T +>z : T | (undefined & T) }; } @@ -98,9 +98,9 @@ class Foo { this.data.toLocaleLowerCase(); >this.data.toLocaleLowerCase() : string >this.data.toLocaleLowerCase : (locales?: string | string[] | undefined) => string ->this.data : (T | undefined) & T +>this.data : T | (undefined & T) >this : this & DatafulFoo ->data : (T | undefined) & T +>data : T | (undefined & T) >toLocaleLowerCase : (locales?: string | string[] | undefined) => string } } From 8913cf0d310f26118406c95301997f97f0b4f759 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 18 May 2022 16:00:58 -0700 Subject: [PATCH 14/30] Add tests --- .../reference/unknownControlFlow.errors.txt | 262 +++++++ .../baselines/reference/unknownControlFlow.js | 519 +++++++++++++ .../reference/unknownControlFlow.symbols | 609 ++++++++++++++++ .../reference/unknownControlFlow.types | 690 ++++++++++++++++++ .../types/unknown/unknownControlFlow.ts | 258 +++++++ 5 files changed, 2338 insertions(+) create mode 100644 tests/baselines/reference/unknownControlFlow.errors.txt create mode 100644 tests/baselines/reference/unknownControlFlow.js create mode 100644 tests/baselines/reference/unknownControlFlow.symbols create mode 100644 tests/baselines/reference/unknownControlFlow.types create mode 100644 tests/cases/conformance/types/unknown/unknownControlFlow.ts diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt new file mode 100644 index 0000000000000..0559ea8de686f --- /dev/null +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -0,0 +1,262 @@ +tests/cases/conformance/types/unknown/unknownControlFlow.ts(14,9): error TS2322: Type 'unknown' is not assignable to type '{}'. + + +==== tests/cases/conformance/types/unknown/unknownControlFlow.ts (1 errors) ==== + type T01 = {} & string; // string + type T02 = {} & 'a'; // 'a' + type T03 = {} & object; // object + type T04 = {} & { x: number }; // { x: number } + type T05 = {} & null; // never + type T06 = {} & undefined; // never + type T07 = undefined & void; // undefined + + type ThisNode = {}; + type ThatNode = {}; + type ThisOrThatNode = ThisNode | ThatNode; + + function f01(u: unknown) { + let x1: {} = u; // Error + ~~ +!!! error TS2322: Type 'unknown' is not assignable to type '{}'. + let x2: {} | null | undefined = u; + let x3: {} | { x: string } | null | undefined = u; + let x4: ThisOrThatNode | null | undefined = u; + } + + function f10(x: unknown) { + if (x) { + x; // {} + } + else { + x; // unknown + } + if (!x) { + x; // unknown + } + else { + x; // {} + } + } + + function f11(x: T) { + if (x) { + x; // T & {} + } + else { + x; // T + } + if (!x) { + x; // T + } + else { + x; // T & {} + } + } + + function f12(x: T) { + if (x) { + x; // T + } + else { + x; // T + } + } + + function f20(x: unknown) { + if (x !== undefined) { + x; // {} | null + } + else { + x; // undefined + } + if (x !== null) { + x; // {} | undefined + } + else { + x; // null + } + if (x !== undefined && x !== null) { + x; // {} + } + else { + x; // null | undefined + } + if (x != undefined) { + x; // {} + } + else { + x; // null | undefined + } + if (x != null) { + x; // {} + } + else { + x; // null | undefined + } + } + + function f21(x: T) { + if (x !== undefined) { + x; // T & ({} | null) + } + else { + x; // T + } + if (x !== null) { + x; // T & ({} | undefined) + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } + } + + function f22(x: T) { + if (x !== undefined) { + x; // T & {} + } + else { + x; // T + } + if (x !== null) { + x; // T + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } + } + + function f23(x: T | undefined | null) { + if (x !== undefined) { + x; // T & {} | null + } + if (x !== null) { + x; // T & {} | undefined + } + if (x != undefined) { + x; // T & {} + } + if (x != null) { + x; // T & {} + } + } + + function f30(x: {}) { + if (typeof x === "object") { + x; // object + } + } + + function f31(x: T) { + if (typeof x === "object") { + x; // T & object | T & null + } + if (x && typeof x === "object") { + x; // T & object + } + if (typeof x === "object" && x) { + x; // T & object + } + } + + function f32(x: T) { + if (typeof x === "object") { + x; // T & object + } + } + + function possiblyNull(x: T) { + return !!true ? x : null; // T | null + } + + function possiblyUndefined(x: T) { + return !!true ? x : undefined; // T | undefined + } + + function possiblyNullOrUndefined(x: T) { + return possiblyUndefined(possiblyNull(x)); // T | null | undefined + } + + function ensureNotNull(x: T) { + if (x === null) throw Error(); + return x; // T & ({} | undefined) + } + + function ensureNotUndefined(x: T) { + if (x === undefined) throw Error(); + return x; // T & ({} | null) + } + + function ensureNotNullOrUndefined(x: T) { + return ensureNotUndefined(ensureNotNull(x)); // T & {} + } + + function f40(a: string | undefined, b: number | null | undefined) { + let a1 = ensureNotNullOrUndefined(a); // string + let b1 = ensureNotNullOrUndefined(b); // number + } + + type QQ = NonNullable>>; + + function f41(a: T) { + let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} + let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} + let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined + let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null + let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} + let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined + let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null + let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined + let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null + } + + // Repro from #48468 + + function deepEquals(a: T, b: T): boolean { + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { + return false; + } + if (Array.isArray(a) || Array.isArray(b)) { + return false; + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here + return false; + } + return true; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unknownControlFlow.js b/tests/baselines/reference/unknownControlFlow.js new file mode 100644 index 0000000000000..ee67697cc08a1 --- /dev/null +++ b/tests/baselines/reference/unknownControlFlow.js @@ -0,0 +1,519 @@ +//// [unknownControlFlow.ts] +type T01 = {} & string; // string +type T02 = {} & 'a'; // 'a' +type T03 = {} & object; // object +type T04 = {} & { x: number }; // { x: number } +type T05 = {} & null; // never +type T06 = {} & undefined; // never +type T07 = undefined & void; // undefined + +type ThisNode = {}; +type ThatNode = {}; +type ThisOrThatNode = ThisNode | ThatNode; + +function f01(u: unknown) { + let x1: {} = u; // Error + let x2: {} | null | undefined = u; + let x3: {} | { x: string } | null | undefined = u; + let x4: ThisOrThatNode | null | undefined = u; +} + +function f10(x: unknown) { + if (x) { + x; // {} + } + else { + x; // unknown + } + if (!x) { + x; // unknown + } + else { + x; // {} + } +} + +function f11(x: T) { + if (x) { + x; // T & {} + } + else { + x; // T + } + if (!x) { + x; // T + } + else { + x; // T & {} + } +} + +function f12(x: T) { + if (x) { + x; // T + } + else { + x; // T + } +} + +function f20(x: unknown) { + if (x !== undefined) { + x; // {} | null + } + else { + x; // undefined + } + if (x !== null) { + x; // {} | undefined + } + else { + x; // null + } + if (x !== undefined && x !== null) { + x; // {} + } + else { + x; // null | undefined + } + if (x != undefined) { + x; // {} + } + else { + x; // null | undefined + } + if (x != null) { + x; // {} + } + else { + x; // null | undefined + } +} + +function f21(x: T) { + if (x !== undefined) { + x; // T & ({} | null) + } + else { + x; // T + } + if (x !== null) { + x; // T & ({} | undefined) + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} + +function f22(x: T) { + if (x !== undefined) { + x; // T & {} + } + else { + x; // T + } + if (x !== null) { + x; // T + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} + +function f23(x: T | undefined | null) { + if (x !== undefined) { + x; // T & {} | null + } + if (x !== null) { + x; // T & {} | undefined + } + if (x != undefined) { + x; // T & {} + } + if (x != null) { + x; // T & {} + } +} + +function f30(x: {}) { + if (typeof x === "object") { + x; // object + } +} + +function f31(x: T) { + if (typeof x === "object") { + x; // T & object | T & null + } + if (x && typeof x === "object") { + x; // T & object + } + if (typeof x === "object" && x) { + x; // T & object + } +} + +function f32(x: T) { + if (typeof x === "object") { + x; // T & object + } +} + +function possiblyNull(x: T) { + return !!true ? x : null; // T | null +} + +function possiblyUndefined(x: T) { + return !!true ? x : undefined; // T | undefined +} + +function possiblyNullOrUndefined(x: T) { + return possiblyUndefined(possiblyNull(x)); // T | null | undefined +} + +function ensureNotNull(x: T) { + if (x === null) throw Error(); + return x; // T & ({} | undefined) +} + +function ensureNotUndefined(x: T) { + if (x === undefined) throw Error(); + return x; // T & ({} | null) +} + +function ensureNotNullOrUndefined(x: T) { + return ensureNotUndefined(ensureNotNull(x)); // T & {} +} + +function f40(a: string | undefined, b: number | null | undefined) { + let a1 = ensureNotNullOrUndefined(a); // string + let b1 = ensureNotNullOrUndefined(b); // number +} + +type QQ = NonNullable>>; + +function f41(a: T) { + let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} + let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} + let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined + let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null + let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} + let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined + let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null + let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined + let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null +} + +// Repro from #48468 + +function deepEquals(a: T, b: T): boolean { + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { + return false; + } + if (Array.isArray(a) || Array.isArray(b)) { + return false; + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here + return false; + } + return true; +} + + +//// [unknownControlFlow.js] +"use strict"; +function f01(u) { + var x1 = u; // Error + var x2 = u; + var x3 = u; + var x4 = u; +} +function f10(x) { + if (x) { + x; // {} + } + else { + x; // unknown + } + if (!x) { + x; // unknown + } + else { + x; // {} + } +} +function f11(x) { + if (x) { + x; // T & {} + } + else { + x; // T + } + if (!x) { + x; // T + } + else { + x; // T & {} + } +} +function f12(x) { + if (x) { + x; // T + } + else { + x; // T + } +} +function f20(x) { + if (x !== undefined) { + x; // {} | null + } + else { + x; // undefined + } + if (x !== null) { + x; // {} | undefined + } + else { + x; // null + } + if (x !== undefined && x !== null) { + x; // {} + } + else { + x; // null | undefined + } + if (x != undefined) { + x; // {} + } + else { + x; // null | undefined + } + if (x != null) { + x; // {} + } + else { + x; // null | undefined + } +} +function f21(x) { + if (x !== undefined) { + x; // T & ({} | null) + } + else { + x; // T + } + if (x !== null) { + x; // T & ({} | undefined) + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} +function f22(x) { + if (x !== undefined) { + x; // T & {} + } + else { + x; // T + } + if (x !== null) { + x; // T + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} +function f23(x) { + if (x !== undefined) { + x; // T & {} | null + } + if (x !== null) { + x; // T & {} | undefined + } + if (x != undefined) { + x; // T & {} + } + if (x != null) { + x; // T & {} + } +} +function f30(x) { + if (typeof x === "object") { + x; // object + } +} +function f31(x) { + if (typeof x === "object") { + x; // T & object | T & null + } + if (x && typeof x === "object") { + x; // T & object + } + if (typeof x === "object" && x) { + x; // T & object + } +} +function f32(x) { + if (typeof x === "object") { + x; // T & object + } +} +function possiblyNull(x) { + return !!true ? x : null; // T | null +} +function possiblyUndefined(x) { + return !!true ? x : undefined; // T | undefined +} +function possiblyNullOrUndefined(x) { + return possiblyUndefined(possiblyNull(x)); // T | null | undefined +} +function ensureNotNull(x) { + if (x === null) + throw Error(); + return x; // T & ({} | undefined) +} +function ensureNotUndefined(x) { + if (x === undefined) + throw Error(); + return x; // T & ({} | null) +} +function ensureNotNullOrUndefined(x) { + return ensureNotUndefined(ensureNotNull(x)); // T & {} +} +function f40(a, b) { + var a1 = ensureNotNullOrUndefined(a); // string + var b1 = ensureNotNullOrUndefined(b); // number +} +function f41(a) { + var a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} + var a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} + var a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined + var a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null + var a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} + var a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined + var a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null + var a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined + var a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null +} +// Repro from #48468 +function deepEquals(a, b) { + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { + return false; + } + if (Array.isArray(a) || Array.isArray(b)) { + return false; + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here + return false; + } + return true; +} + + +//// [unknownControlFlow.d.ts] +declare type T01 = {} & string; +declare type T02 = {} & 'a'; +declare type T03 = {} & object; +declare type T04 = {} & { + x: number; +}; +declare type T05 = {} & null; +declare type T06 = {} & undefined; +declare type T07 = undefined & void; +declare type ThisNode = {}; +declare type ThatNode = {}; +declare type ThisOrThatNode = ThisNode | ThatNode; +declare function f01(u: unknown): void; +declare function f10(x: unknown): void; +declare function f11(x: T): void; +declare function f12(x: T): void; +declare function f20(x: unknown): void; +declare function f21(x: T): void; +declare function f22(x: T): void; +declare function f23(x: T | undefined | null): void; +declare function f30(x: {}): void; +declare function f31(x: T): void; +declare function f32(x: T): void; +declare function possiblyNull(x: T): T | null; +declare function possiblyUndefined(x: T): T | undefined; +declare function possiblyNullOrUndefined(x: T): T | null | undefined; +declare function ensureNotNull(x: T): T & ({} | undefined); +declare function ensureNotUndefined(x: T): T & ({} | null); +declare function ensureNotNullOrUndefined(x: T): T & {}; +declare function f40(a: string | undefined, b: number | null | undefined): void; +declare type QQ = NonNullable>>; +declare function f41(a: T): void; +declare function deepEquals(a: T, b: T): boolean; diff --git a/tests/baselines/reference/unknownControlFlow.symbols b/tests/baselines/reference/unknownControlFlow.symbols new file mode 100644 index 0000000000000..a5f106be81bb8 --- /dev/null +++ b/tests/baselines/reference/unknownControlFlow.symbols @@ -0,0 +1,609 @@ +=== tests/cases/conformance/types/unknown/unknownControlFlow.ts === +type T01 = {} & string; // string +>T01 : Symbol(T01, Decl(unknownControlFlow.ts, 0, 0)) + +type T02 = {} & 'a'; // 'a' +>T02 : Symbol(T02, Decl(unknownControlFlow.ts, 0, 23)) + +type T03 = {} & object; // object +>T03 : Symbol(T03, Decl(unknownControlFlow.ts, 1, 20)) + +type T04 = {} & { x: number }; // { x: number } +>T04 : Symbol(T04, Decl(unknownControlFlow.ts, 2, 23)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 3, 17)) + +type T05 = {} & null; // never +>T05 : Symbol(T05, Decl(unknownControlFlow.ts, 3, 30)) + +type T06 = {} & undefined; // never +>T06 : Symbol(T06, Decl(unknownControlFlow.ts, 4, 21)) + +type T07 = undefined & void; // undefined +>T07 : Symbol(T07, Decl(unknownControlFlow.ts, 5, 26)) + +type ThisNode = {}; +>ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 6, 28)) + +type ThatNode = {}; +>ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 8, 19)) + +type ThisOrThatNode = ThisNode | ThatNode; +>ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 9, 19)) +>ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 6, 28)) +>ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 8, 19)) + +function f01(u: unknown) { +>f01 : Symbol(f01, Decl(unknownControlFlow.ts, 10, 42)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) + + let x1: {} = u; // Error +>x1 : Symbol(x1, Decl(unknownControlFlow.ts, 13, 7)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) + + let x2: {} | null | undefined = u; +>x2 : Symbol(x2, Decl(unknownControlFlow.ts, 14, 7)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) + + let x3: {} | { x: string } | null | undefined = u; +>x3 : Symbol(x3, Decl(unknownControlFlow.ts, 15, 7)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 15, 18)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) + + let x4: ThisOrThatNode | null | undefined = u; +>x4 : Symbol(x4, Decl(unknownControlFlow.ts, 16, 7)) +>ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 9, 19)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +} + +function f10(x: unknown) { +>f10 : Symbol(f10, Decl(unknownControlFlow.ts, 17, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + + if (x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + + x; // {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + } + else { + x; // unknown +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + } + if (!x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + + x; // unknown +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + } + else { + x; // {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) + } +} + +function f11(x: T) { +>f11 : Symbol(f11, Decl(unknownControlFlow.ts, 32, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 34, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 34, 13)) + + if (x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + } + if (!x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + } + else { + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) + } +} + +function f12(x: T) { +>f12 : Symbol(f12, Decl(unknownControlFlow.ts, 47, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 49, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 49, 13)) + + if (x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) + + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) + } +} + +function f20(x: unknown) { +>f20 : Symbol(f20, Decl(unknownControlFlow.ts, 56, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + + if (x !== undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>undefined : Symbol(undefined) + + x; // {} | null +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + else { + x; // undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + if (x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + + x; // {} | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + else { + x; // null +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + if (x !== undefined && x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + + x; // {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + else { + x; // null | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + if (x != undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>undefined : Symbol(undefined) + + x; // {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + else { + x; // null | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + if (x != null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + + x; // {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } + else { + x; // null | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) + } +} + +function f21(x: T) { +>f21 : Symbol(f21, Decl(unknownControlFlow.ts, 89, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 91, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 91, 13)) + + if (x !== undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>undefined : Symbol(undefined) + + x; // T & ({} | null) +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + if (x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + + x; // T & ({} | undefined) +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + if (x !== undefined && x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + if (x != undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>undefined : Symbol(undefined) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + if (x != null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) + } +} + +function f22(x: T) { +>f22 : Symbol(f22, Decl(unknownControlFlow.ts, 122, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 124, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 124, 13)) + + if (x !== undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>undefined : Symbol(undefined) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + if (x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + if (x !== undefined && x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>undefined : Symbol(undefined) +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + if (x != undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>undefined : Symbol(undefined) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + if (x != null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } + else { + x; // T +>x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) + } +} + +function f23(x: T | undefined | null) { +>f23 : Symbol(f23, Decl(unknownControlFlow.ts, 155, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 157, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 157, 13)) + + if (x !== undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>undefined : Symbol(undefined) + + x; // T & {} | null +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + } + if (x !== null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + + x; // T & {} | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + } + if (x != undefined) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>undefined : Symbol(undefined) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + } + if (x != null) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + + x; // T & {} +>x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) + } +} + +function f30(x: {}) { +>f30 : Symbol(f30, Decl(unknownControlFlow.ts, 170, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) + + if (typeof x === "object") { +>x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) + + x; // object +>x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) + } +} + +function f31(x: T) { +>f31 : Symbol(f31, Decl(unknownControlFlow.ts, 176, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 178, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 178, 13)) + + if (typeof x === "object") { +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + + x; // T & object | T & null +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + } + if (x && typeof x === "object") { +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + + x; // T & object +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + } + if (typeof x === "object" && x) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + + x; // T & object +>x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) + } +} + +function f32(x: T) { +>f32 : Symbol(f32, Decl(unknownControlFlow.ts, 188, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 190, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 190, 13)) + + if (typeof x === "object") { +>x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) + + x; // T & object +>x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) + } +} + +function possiblyNull(x: T) { +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 196, 22)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 196, 25)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 196, 22)) + + return !!true ? x : null; // T | null +>x : Symbol(x, Decl(unknownControlFlow.ts, 196, 25)) +} + +function possiblyUndefined(x: T) { +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 200, 27)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 200, 30)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 200, 27)) + + return !!true ? x : undefined; // T | undefined +>x : Symbol(x, Decl(unknownControlFlow.ts, 200, 30)) +>undefined : Symbol(undefined) +} + +function possiblyNullOrUndefined(x: T) { +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 204, 33)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 204, 36)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 204, 33)) + + return possiblyUndefined(possiblyNull(x)); // T | null | undefined +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 204, 36)) +} + +function ensureNotNull(x: T) { +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 208, 23)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 208, 23)) + + if (x === null) throw Error(); +>x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return x; // T & ({} | undefined) +>x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) +} + +function ensureNotUndefined(x: T) { +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 213, 28)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 213, 28)) + + if (x === undefined) throw Error(); +>x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) +>undefined : Symbol(undefined) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return x; // T & ({} | null) +>x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) +} + +function ensureNotNullOrUndefined(x: T) { +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 218, 34)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 218, 37)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 218, 34)) + + return ensureNotUndefined(ensureNotNull(x)); // T & {} +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 218, 37)) +} + +function f40(a: string | undefined, b: number | null | undefined) { +>f40 : Symbol(f40, Decl(unknownControlFlow.ts, 220, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 222, 13)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 222, 35)) + + let a1 = ensureNotNullOrUndefined(a); // string +>a1 : Symbol(a1, Decl(unknownControlFlow.ts, 223, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 222, 13)) + + let b1 = ensureNotNullOrUndefined(b); // number +>b1 : Symbol(b1, Decl(unknownControlFlow.ts, 224, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 222, 35)) +} + +type QQ = NonNullable>>; +>QQ : Symbol(QQ, Decl(unknownControlFlow.ts, 225, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 227, 8)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 227, 8)) + +function f41(a: T) { +>f41 : Symbol(f41, Decl(unknownControlFlow.ts, 227, 54)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 229, 13)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 229, 13)) + + let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} +>a1 : Symbol(a1, Decl(unknownControlFlow.ts, 230, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} +>a2 : Symbol(a2, Decl(unknownControlFlow.ts, 231, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined +>a3 : Symbol(a3, Decl(unknownControlFlow.ts, 232, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null +>a4 : Symbol(a4, Decl(unknownControlFlow.ts, 233, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} +>a5 : Symbol(a5, Decl(unknownControlFlow.ts, 234, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined +>a6 : Symbol(a6, Decl(unknownControlFlow.ts, 235, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null +>a7 : Symbol(a7, Decl(unknownControlFlow.ts, 236, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined +>a8 : Symbol(a8, Decl(unknownControlFlow.ts, 237, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) + + let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null +>a9 : Symbol(a9, Decl(unknownControlFlow.ts, 238, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +} + +// Repro from #48468 + +function deepEquals(a: T, b: T): boolean { +>deepEquals : Symbol(deepEquals, Decl(unknownControlFlow.ts, 239, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) + + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { +>a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) + + return false; + } + if (Array.isArray(a) || Array.isArray(b)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) + + return false; + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here +>Object.keys(a).length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>Object.keys(b).length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + + return false; + } + return true; +} + diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types new file mode 100644 index 0000000000000..13610de77868f --- /dev/null +++ b/tests/baselines/reference/unknownControlFlow.types @@ -0,0 +1,690 @@ +=== tests/cases/conformance/types/unknown/unknownControlFlow.ts === +type T01 = {} & string; // string +>T01 : string + +type T02 = {} & 'a'; // 'a' +>T02 : "a" + +type T03 = {} & object; // object +>T03 : object + +type T04 = {} & { x: number }; // { x: number } +>T04 : { x: number; } +>x : number + +type T05 = {} & null; // never +>T05 : never +>null : null + +type T06 = {} & undefined; // never +>T06 : never + +type T07 = undefined & void; // undefined +>T07 : undefined + +type ThisNode = {}; +>ThisNode : {} + +type ThatNode = {}; +>ThatNode : {} + +type ThisOrThatNode = ThisNode | ThatNode; +>ThisOrThatNode : ThisNode | ThatNode + +function f01(u: unknown) { +>f01 : (u: unknown) => void +>u : unknown + + let x1: {} = u; // Error +>x1 : {} +>u : unknown + + let x2: {} | null | undefined = u; +>x2 : {} | null | undefined +>null : null +>u : unknown + + let x3: {} | { x: string } | null | undefined = u; +>x3 : {} | { x: string; } | null | undefined +>x : string +>null : null +>u : unknown + + let x4: ThisOrThatNode | null | undefined = u; +>x4 : ThisOrThatNode | null | undefined +>null : null +>u : unknown +} + +function f10(x: unknown) { +>f10 : (x: unknown) => void +>x : unknown + + if (x) { +>x : unknown + + x; // {} +>x : {} + } + else { + x; // unknown +>x : unknown + } + if (!x) { +>!x : boolean +>x : unknown + + x; // unknown +>x : unknown + } + else { + x; // {} +>x : {} + } +} + +function f11(x: T) { +>f11 : (x: T) => void +>x : T + + if (x) { +>x : T + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (!x) { +>!x : boolean +>x : T + + x; // T +>x : T + } + else { + x; // T & {} +>x : T & {} + } +} + +function f12(x: T) { +>f12 : (x: T) => void +>x : T + + if (x) { +>x : T + + x; // T +>x : T + } + else { + x; // T +>x : T + } +} + +function f20(x: unknown) { +>f20 : (x: unknown) => void +>x : unknown + + if (x !== undefined) { +>x !== undefined : boolean +>x : unknown +>undefined : undefined + + x; // {} | null +>x : {} | null + } + else { + x; // undefined +>x : undefined + } + if (x !== null) { +>x !== null : boolean +>x : unknown +>null : null + + x; // {} | undefined +>x : {} | undefined + } + else { + x; // null +>x : null + } + if (x !== undefined && x !== null) { +>x !== undefined && x !== null : boolean +>x !== undefined : boolean +>x : unknown +>undefined : undefined +>x !== null : boolean +>x : {} | null +>null : null + + x; // {} +>x : {} + } + else { + x; // null | undefined +>x : null | undefined + } + if (x != undefined) { +>x != undefined : boolean +>x : unknown +>undefined : undefined + + x; // {} +>x : {} + } + else { + x; // null | undefined +>x : null | undefined + } + if (x != null) { +>x != null : boolean +>x : unknown +>null : null + + x; // {} +>x : {} + } + else { + x; // null | undefined +>x : null | undefined + } +} + +function f21(x: T) { +>f21 : (x: T) => void +>x : T + + if (x !== undefined) { +>x !== undefined : boolean +>x : T +>undefined : undefined + + x; // T & ({} | null) +>x : T & ({} | null) + } + else { + x; // T +>x : T + } + if (x !== null) { +>x !== null : boolean +>x : T +>null : null + + x; // T & ({} | undefined) +>x : T & ({} | undefined) + } + else { + x; // T +>x : T + } + if (x !== undefined && x !== null) { +>x !== undefined && x !== null : boolean +>x !== undefined : boolean +>x : T +>undefined : undefined +>x !== null : boolean +>x : T & ({} | null) +>null : null + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (x != undefined) { +>x != undefined : boolean +>x : T +>undefined : undefined + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (x != null) { +>x != null : boolean +>x : T +>null : null + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } +} + +function f22(x: T) { +>f22 : (x: T) => void +>x : T + + if (x !== undefined) { +>x !== undefined : boolean +>x : T +>undefined : undefined + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (x !== null) { +>x !== null : boolean +>x : T +>null : null + + x; // T +>x : T + } + else { + x; // T +>x : never + } + if (x !== undefined && x !== null) { +>x !== undefined && x !== null : boolean +>x !== undefined : boolean +>x : T +>undefined : undefined +>x !== null : boolean +>x : T & {} +>null : null + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (x != undefined) { +>x != undefined : boolean +>x : T +>undefined : undefined + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } + if (x != null) { +>x != null : boolean +>x : T +>null : null + + x; // T & {} +>x : T & {} + } + else { + x; // T +>x : T + } +} + +function f23(x: T | undefined | null) { +>f23 : (x: T | undefined | null) => void +>x : T | null | undefined +>null : null + + if (x !== undefined) { +>x !== undefined : boolean +>x : T | null | undefined +>undefined : undefined + + x; // T & {} | null +>x : (T & {}) | null + } + if (x !== null) { +>x !== null : boolean +>x : T | null | undefined +>null : null + + x; // T & {} | undefined +>x : (T & {}) | undefined + } + if (x != undefined) { +>x != undefined : boolean +>x : T | null | undefined +>undefined : undefined + + x; // T & {} +>x : T & {} + } + if (x != null) { +>x != null : boolean +>x : T | null | undefined +>null : null + + x; // T & {} +>x : T & {} + } +} + +function f30(x: {}) { +>f30 : (x: {}) => void +>x : {} + + if (typeof x === "object") { +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : {} +>"object" : "object" + + x; // object +>x : object + } +} + +function f31(x: T) { +>f31 : (x: T) => void +>x : T + + if (typeof x === "object") { +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T +>"object" : "object" + + x; // T & object | T & null +>x : (T & null) | (T & object) + } + if (x && typeof x === "object") { +>x && typeof x === "object" : boolean +>x : T +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T & {} +>"object" : "object" + + x; // T & object +>x : T & object + } + if (typeof x === "object" && x) { +>typeof x === "object" && x : false | (T & null) | (T & object) +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T +>"object" : "object" +>x : (T & null) | (T & object) + + x; // T & object +>x : T & object + } +} + +function f32(x: T) { +>f32 : (x: T) => void +>x : T + + if (typeof x === "object") { +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T +>"object" : "object" + + x; // T & object +>x : T & object + } +} + +function possiblyNull(x: T) { +>possiblyNull : (x: T) => T | null +>x : T + + return !!true ? x : null; // T | null +>!!true ? x : null : T | null +>!!true : true +>!true : false +>true : true +>x : T +>null : null +} + +function possiblyUndefined(x: T) { +>possiblyUndefined : (x: T) => T | undefined +>x : T + + return !!true ? x : undefined; // T | undefined +>!!true ? x : undefined : T | undefined +>!!true : true +>!true : false +>true : true +>x : T +>undefined : undefined +} + +function possiblyNullOrUndefined(x: T) { +>possiblyNullOrUndefined : (x: T) => T | null | undefined +>x : T + + return possiblyUndefined(possiblyNull(x)); // T | null | undefined +>possiblyUndefined(possiblyNull(x)) : T | null | undefined +>possiblyUndefined : (x: T) => T | undefined +>possiblyNull(x) : T | null +>possiblyNull : (x: T) => T | null +>x : T +} + +function ensureNotNull(x: T) { +>ensureNotNull : (x: T) => T & ({} | undefined) +>x : T + + if (x === null) throw Error(); +>x === null : boolean +>x : T +>null : null +>Error() : Error +>Error : ErrorConstructor + + return x; // T & ({} | undefined) +>x : T & ({} | undefined) +} + +function ensureNotUndefined(x: T) { +>ensureNotUndefined : (x: T) => T & ({} | null) +>x : T + + if (x === undefined) throw Error(); +>x === undefined : boolean +>x : T +>undefined : undefined +>Error() : Error +>Error : ErrorConstructor + + return x; // T & ({} | null) +>x : T & ({} | null) +} + +function ensureNotNullOrUndefined(x: T) { +>ensureNotNullOrUndefined : (x: T) => T & {} +>x : T + + return ensureNotUndefined(ensureNotNull(x)); // T & {} +>ensureNotUndefined(ensureNotNull(x)) : T & {} +>ensureNotUndefined : (x: T) => T & ({} | null) +>ensureNotNull(x) : T & ({} | undefined) +>ensureNotNull : (x: T) => T & ({} | undefined) +>x : T +} + +function f40(a: string | undefined, b: number | null | undefined) { +>f40 : (a: string | undefined, b: number | null | undefined) => void +>a : string | undefined +>b : number | null | undefined +>null : null + + let a1 = ensureNotNullOrUndefined(a); // string +>a1 : string +>ensureNotNullOrUndefined(a) : string +>ensureNotNullOrUndefined : (x: T) => T & {} +>a : string | undefined + + let b1 = ensureNotNullOrUndefined(b); // number +>b1 : number +>ensureNotNullOrUndefined(b) : number +>ensureNotNullOrUndefined : (x: T) => T & {} +>b : number | null | undefined +} + +type QQ = NonNullable>>; +>QQ : QQ + +function f41(a: T) { +>f41 : (a: T) => void +>a : T + + let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} +>a1 : T & {} +>ensureNotUndefined(ensureNotNull(a)) : T & {} +>ensureNotUndefined : (x: T) => T & ({} | null) +>ensureNotNull(a) : T & ({} | undefined) +>ensureNotNull : (x: T) => T & ({} | undefined) +>a : T + + let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} +>a2 : T & {} +>ensureNotNull(ensureNotUndefined(a)) : T & {} +>ensureNotNull : (x: T) => T & ({} | undefined) +>ensureNotUndefined(a) : T & ({} | null) +>ensureNotUndefined : (x: T) => T & ({} | null) +>a : T + + let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined +>a3 : (T & undefined) | (T & {}) +>ensureNotNull(ensureNotNull(a)) : (T & undefined) | (T & {}) +>ensureNotNull : (x: T) => T & ({} | undefined) +>ensureNotNull(a) : T & ({} | undefined) +>ensureNotNull : (x: T) => T & ({} | undefined) +>a : T + + let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null +>a4 : (T & {}) | (T & null) +>ensureNotUndefined(ensureNotUndefined(a)) : (T & {}) | (T & null) +>ensureNotUndefined : (x: T) => T & ({} | null) +>ensureNotUndefined(a) : T & ({} | null) +>ensureNotUndefined : (x: T) => T & ({} | null) +>a : T + + let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} +>a5 : T & {} +>ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)) : T & {} +>ensureNotNullOrUndefined : (x: T) => T & {} +>ensureNotNullOrUndefined(a) : T & {} +>ensureNotNullOrUndefined : (x: T) => T & {} +>a : T + + let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined +>a6 : (T & {}) | undefined +>ensureNotNull(possiblyNullOrUndefined(a)) : (T & {}) | undefined +>ensureNotNull : (x: T) => T & ({} | undefined) +>possiblyNullOrUndefined(a) : T | null | undefined +>possiblyNullOrUndefined : (x: T) => T | null | undefined +>a : T + + let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null +>a7 : (T & {}) | null +>ensureNotUndefined(possiblyNullOrUndefined(a)) : (T & {}) | null +>ensureNotUndefined : (x: T) => T & ({} | null) +>possiblyNullOrUndefined(a) : T | null | undefined +>possiblyNullOrUndefined : (x: T) => T | null | undefined +>a : T + + let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined +>a8 : (T & {}) | undefined +>ensureNotNull(possiblyUndefined(a)) : (T & {}) | undefined +>ensureNotNull : (x: T) => T & ({} | undefined) +>possiblyUndefined(a) : T | undefined +>possiblyUndefined : (x: T) => T | undefined +>a : T + + let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null +>a9 : (T & {}) | null +>ensureNotUndefined(possiblyNull(a)) : (T & {}) | null +>ensureNotUndefined : (x: T) => T & ({} | null) +>possiblyNull(a) : T | null +>possiblyNull : (x: T) => T | null +>a : T +} + +// Repro from #48468 + +function deepEquals(a: T, b: T): boolean { +>deepEquals : (a: T, b: T) => boolean +>a : T +>b : T + + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { +>typeof a !== 'object' || typeof b !== 'object' || !a || !b : boolean +>typeof a !== 'object' || typeof b !== 'object' || !a : boolean +>typeof a !== 'object' || typeof b !== 'object' : boolean +>typeof a !== 'object' : boolean +>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>a : T +>'object' : "object" +>typeof b !== 'object' : boolean +>typeof b : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>b : T +>'object' : "object" +>!a : boolean +>a : (T & null) | (T & object) +>!b : boolean +>b : (T & null) | (T & object) + + return false; +>false : false + } + if (Array.isArray(a) || Array.isArray(b)) { +>Array.isArray(a) || Array.isArray(b) : boolean +>Array.isArray(a) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>a : T & object +>Array.isArray(b) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>b : T & object + + return false; +>false : false + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here +>Object.keys(a).length !== Object.keys(b).length : boolean +>Object.keys(a).length : number +>Object.keys(a) : string[] +>Object.keys : (o: object) => string[] +>Object : ObjectConstructor +>keys : (o: object) => string[] +>a : T & object +>length : number +>Object.keys(b).length : number +>Object.keys(b) : string[] +>Object.keys : (o: object) => string[] +>Object : ObjectConstructor +>keys : (o: object) => string[] +>b : T & object +>length : number + + return false; +>false : false + } + return true; +>true : true +} + diff --git a/tests/cases/conformance/types/unknown/unknownControlFlow.ts b/tests/cases/conformance/types/unknown/unknownControlFlow.ts new file mode 100644 index 0000000000000..89c677b6d1eb1 --- /dev/null +++ b/tests/cases/conformance/types/unknown/unknownControlFlow.ts @@ -0,0 +1,258 @@ +// @strict: true +// @declaration: true + +type T01 = {} & string; // string +type T02 = {} & 'a'; // 'a' +type T03 = {} & object; // object +type T04 = {} & { x: number }; // { x: number } +type T05 = {} & null; // never +type T06 = {} & undefined; // never +type T07 = undefined & void; // undefined + +type ThisNode = {}; +type ThatNode = {}; +type ThisOrThatNode = ThisNode | ThatNode; + +function f01(u: unknown) { + let x1: {} = u; // Error + let x2: {} | null | undefined = u; + let x3: {} | { x: string } | null | undefined = u; + let x4: ThisOrThatNode | null | undefined = u; +} + +function f10(x: unknown) { + if (x) { + x; // {} + } + else { + x; // unknown + } + if (!x) { + x; // unknown + } + else { + x; // {} + } +} + +function f11(x: T) { + if (x) { + x; // T & {} + } + else { + x; // T + } + if (!x) { + x; // T + } + else { + x; // T & {} + } +} + +function f12(x: T) { + if (x) { + x; // T + } + else { + x; // T + } +} + +function f20(x: unknown) { + if (x !== undefined) { + x; // {} | null + } + else { + x; // undefined + } + if (x !== null) { + x; // {} | undefined + } + else { + x; // null + } + if (x !== undefined && x !== null) { + x; // {} + } + else { + x; // null | undefined + } + if (x != undefined) { + x; // {} + } + else { + x; // null | undefined + } + if (x != null) { + x; // {} + } + else { + x; // null | undefined + } +} + +function f21(x: T) { + if (x !== undefined) { + x; // T & ({} | null) + } + else { + x; // T + } + if (x !== null) { + x; // T & ({} | undefined) + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} + +function f22(x: T) { + if (x !== undefined) { + x; // T & {} + } + else { + x; // T + } + if (x !== null) { + x; // T + } + else { + x; // T + } + if (x !== undefined && x !== null) { + x; // T & {} + } + else { + x; // T + } + if (x != undefined) { + x; // T & {} + } + else { + x; // T + } + if (x != null) { + x; // T & {} + } + else { + x; // T + } +} + +function f23(x: T | undefined | null) { + if (x !== undefined) { + x; // T & {} | null + } + if (x !== null) { + x; // T & {} | undefined + } + if (x != undefined) { + x; // T & {} + } + if (x != null) { + x; // T & {} + } +} + +function f30(x: {}) { + if (typeof x === "object") { + x; // object + } +} + +function f31(x: T) { + if (typeof x === "object") { + x; // T & object | T & null + } + if (x && typeof x === "object") { + x; // T & object + } + if (typeof x === "object" && x) { + x; // T & object + } +} + +function f32(x: T) { + if (typeof x === "object") { + x; // T & object + } +} + +function possiblyNull(x: T) { + return !!true ? x : null; // T | null +} + +function possiblyUndefined(x: T) { + return !!true ? x : undefined; // T | undefined +} + +function possiblyNullOrUndefined(x: T) { + return possiblyUndefined(possiblyNull(x)); // T | null | undefined +} + +function ensureNotNull(x: T) { + if (x === null) throw Error(); + return x; // T & ({} | undefined) +} + +function ensureNotUndefined(x: T) { + if (x === undefined) throw Error(); + return x; // T & ({} | null) +} + +function ensureNotNullOrUndefined(x: T) { + return ensureNotUndefined(ensureNotNull(x)); // T & {} +} + +function f40(a: string | undefined, b: number | null | undefined) { + let a1 = ensureNotNullOrUndefined(a); // string + let b1 = ensureNotNullOrUndefined(b); // number +} + +type QQ = NonNullable>>; + +function f41(a: T) { + let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} + let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} + let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined + let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null + let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} + let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined + let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null + let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined + let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null +} + +// Repro from #48468 + +function deepEquals(a: T, b: T): boolean { + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { + return false; + } + if (Array.isArray(a) || Array.isArray(b)) { + return false; + } + if (Object.keys(a).length !== Object.keys(b).length) { // Error here + return false; + } + return true; +} From dbce210e3177aeebdb160a1df806e29f33b0f87a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 19 May 2022 10:38:50 -0700 Subject: [PATCH 15/30] Only attach origin type when it is shorter than union itself --- src/compiler/checker.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a1379f1b9ab5c..05168e819dfd0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5041,8 +5041,7 @@ namespace ts { (type === markerSubType ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?"; return factory.createTypeReferenceNode(factory.createIdentifier(name), /*typeArguments*/ undefined); } - // Switch to the union origin only if it has fewer constituents than the union itself. - if (type.flags & TypeFlags.Union && (type as UnionType).origin && getConstituentCount(type) > getConstituentCount((type as UnionType).origin!)) { + if (type.flags & TypeFlags.Union && (type as UnionType).origin) { type = (type as UnionType).origin!; } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { @@ -15042,8 +15041,9 @@ namespace ts { } const constituents = getCrossProductIntersections(typeSet); // We attach a denormalized origin type when at least one constituent of the cross-product union is an - // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions). - const origin = some(constituents, t => !!(t.flags & TypeFlags.Intersection)) ? createOriginUnionOrIntersectionType(TypeFlags.Intersection, typeSet) : undefined; + // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions) and + // the denormalized origin has fewer constituents than the union itself. + const origin = some(constituents, t => !!(t.flags & TypeFlags.Intersection)) && getConstituentCountOfTypes(constituents) > getConstituentCountOfTypes(typeSet) ? createOriginUnionOrIntersectionType(TypeFlags.Intersection, typeSet) : undefined; result = getUnionType(constituents, UnionReduction.Literal, aliasSymbol, aliasTypeArguments, origin); } } @@ -15090,8 +15090,13 @@ namespace ts { } function getConstituentCount(type: Type): number { - return !(type.flags & TypeFlags.UnionOrIntersection) ? 1 : reduceLeft((type as UnionOrIntersectionType).types, - (n, t) => n + (t.aliasSymbol ? 1 : getConstituentCount(t.flags & TypeFlags.Union && (t as UnionType).origin || t)), 0); + return !(type.flags & TypeFlags.UnionOrIntersection) || type.aliasSymbol ? 1 : + type.flags & TypeFlags.Union && (type as UnionType).origin ? getConstituentCount((type as UnionType).origin!) : + getConstituentCountOfTypes((type as UnionOrIntersectionType).types); + } + + function getConstituentCountOfTypes(types: Type[]): number { + return reduceLeft(types, (n, t) => n + getConstituentCount(t), 0); } function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { From 1913c94c2077e9228ffdb361327eb84edb21cbc8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 08:32:05 -0700 Subject: [PATCH 16/30] Specially preserve string & {}, number & {}, bigint & {} --- src/compiler/checker.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 05168e819dfd0..b5246b2fae2f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14962,7 +14962,7 @@ namespace ts { // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. - function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { + function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], noSupertypeReduction?: boolean): Type { const typeMembershipMap: ESMap = new Map(); const includes = addTypesToIntersection(typeMembershipMap, 0, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); @@ -15002,7 +15002,7 @@ namespace ts { includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || includes & TypeFlags.Void && includes & TypeFlags.Undefined || includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) { - removeRedundantSupertypes(typeSet, includes); + if (!noSupertypeReduction) removeRedundantSupertypes(typeSet, includes); } if (includes & TypeFlags.IncludesMissingType) { typeSet[typeSet.indexOf(undefinedType)] = missingType; @@ -15103,8 +15103,9 @@ namespace ts { const links = getNodeLinks(node); if (!links.resolvedType) { const aliasSymbol = getAliasSymbolForTypeNode(node); - links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode), - aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol)); + const types = map(node.types, getTypeFromTypeNode); + const noSupertypeReduction = types.length === 2 && !!(types[0].flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && types[1] === emptyTypeLiteralType; + links.resolvedType = getIntersectionType(types, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol), noSupertypeReduction); } return links.resolvedType; } From 8c7a38b116ef0850be2ecdd24e01b39e1eb1f309 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 08:32:22 -0700 Subject: [PATCH 17/30] Accept new baselines --- tests/baselines/reference/templateLiteralTypes2.types | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/templateLiteralTypes2.types b/tests/baselines/reference/templateLiteralTypes2.types index cb8443cd9ca5c..e771c63c5357c 100644 --- a/tests/baselines/reference/templateLiteralTypes2.types +++ b/tests/baselines/reference/templateLiteralTypes2.types @@ -402,13 +402,13 @@ const interpolatedStyle = { rotate: 12 }; function C2(transform: "-moz-initial" | (string & {})) { return 12; } >C2 : (transform: "-moz-initial" | (string & {})) => number ->transform : string +>transform : (string & {}) | "-moz-initial" >12 : 12 C2(`rotate(${interpolatedStyle.rotate}dig)`); >C2(`rotate(${interpolatedStyle.rotate}dig)`) : number ->C2 : (transform: string) => number ->`rotate(${interpolatedStyle.rotate}dig)` : string +>C2 : (transform: (string & {}) | "-moz-initial") => number +>`rotate(${interpolatedStyle.rotate}dig)` : `rotate(${number}dig)` >interpolatedStyle.rotate : number >interpolatedStyle : { rotate: number; } >rotate : number From 6f18e909b88dbc3843a0095490b32478233e1acf Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 08:32:34 -0700 Subject: [PATCH 18/30] Add additional tests --- .../reference/unknownControlFlow.errors.txt | 6 +- .../baselines/reference/unknownControlFlow.js | 7 + .../reference/unknownControlFlow.symbols | 481 +++++++++--------- .../reference/unknownControlFlow.types | 9 + .../types/unknown/unknownControlFlow.ts | 4 + 5 files changed, 270 insertions(+), 237 deletions(-) diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index 0559ea8de686f..540c0134e2d02 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/types/unknown/unknownControlFlow.ts(14,9): error TS2322: Type 'unknown' is not assignable to type '{}'. +tests/cases/conformance/types/unknown/unknownControlFlow.ts(18,9): error TS2322: Type 'unknown' is not assignable to type '{}'. ==== tests/cases/conformance/types/unknown/unknownControlFlow.ts (1 errors) ==== @@ -10,6 +10,10 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(14,9): error TS2322: type T06 = {} & undefined; // never type T07 = undefined & void; // undefined + type T10 = string & {}; // Specially preserved + type T11 = number & {}; // Specially preserved + type T12 = bigint & {}; // Specially preserved + type ThisNode = {}; type ThatNode = {}; type ThisOrThatNode = ThisNode | ThatNode; diff --git a/tests/baselines/reference/unknownControlFlow.js b/tests/baselines/reference/unknownControlFlow.js index ee67697cc08a1..0cc3d1d0d779b 100644 --- a/tests/baselines/reference/unknownControlFlow.js +++ b/tests/baselines/reference/unknownControlFlow.js @@ -7,6 +7,10 @@ type T05 = {} & null; // never type T06 = {} & undefined; // never type T07 = undefined & void; // undefined +type T10 = string & {}; // Specially preserved +type T11 = number & {}; // Specially preserved +type T12 = bigint & {}; // Specially preserved + type ThisNode = {}; type ThatNode = {}; type ThisOrThatNode = ThisNode | ThatNode; @@ -493,6 +497,9 @@ declare type T04 = {} & { declare type T05 = {} & null; declare type T06 = {} & undefined; declare type T07 = undefined & void; +declare type T10 = string & {}; +declare type T11 = number & {}; +declare type T12 = bigint & {}; declare type ThisNode = {}; declare type ThatNode = {}; declare type ThisOrThatNode = ThisNode | ThatNode; diff --git a/tests/baselines/reference/unknownControlFlow.symbols b/tests/baselines/reference/unknownControlFlow.symbols index a5f106be81bb8..eb938f3d406f3 100644 --- a/tests/baselines/reference/unknownControlFlow.symbols +++ b/tests/baselines/reference/unknownControlFlow.symbols @@ -21,558 +21,567 @@ type T06 = {} & undefined; // never type T07 = undefined & void; // undefined >T07 : Symbol(T07, Decl(unknownControlFlow.ts, 5, 26)) +type T10 = string & {}; // Specially preserved +>T10 : Symbol(T10, Decl(unknownControlFlow.ts, 6, 28)) + +type T11 = number & {}; // Specially preserved +>T11 : Symbol(T11, Decl(unknownControlFlow.ts, 8, 23)) + +type T12 = bigint & {}; // Specially preserved +>T12 : Symbol(T12, Decl(unknownControlFlow.ts, 9, 23)) + type ThisNode = {}; ->ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 6, 28)) +>ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 10, 23)) type ThatNode = {}; ->ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 8, 19)) +>ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 12, 19)) type ThisOrThatNode = ThisNode | ThatNode; ->ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 9, 19)) ->ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 6, 28)) ->ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 8, 19)) +>ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 13, 19)) +>ThisNode : Symbol(ThisNode, Decl(unknownControlFlow.ts, 10, 23)) +>ThatNode : Symbol(ThatNode, Decl(unknownControlFlow.ts, 12, 19)) function f01(u: unknown) { ->f01 : Symbol(f01, Decl(unknownControlFlow.ts, 10, 42)) ->u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +>f01 : Symbol(f01, Decl(unknownControlFlow.ts, 14, 42)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 16, 13)) let x1: {} = u; // Error ->x1 : Symbol(x1, Decl(unknownControlFlow.ts, 13, 7)) ->u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +>x1 : Symbol(x1, Decl(unknownControlFlow.ts, 17, 7)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 16, 13)) let x2: {} | null | undefined = u; ->x2 : Symbol(x2, Decl(unknownControlFlow.ts, 14, 7)) ->u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +>x2 : Symbol(x2, Decl(unknownControlFlow.ts, 18, 7)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 16, 13)) let x3: {} | { x: string } | null | undefined = u; ->x3 : Symbol(x3, Decl(unknownControlFlow.ts, 15, 7)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 15, 18)) ->u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +>x3 : Symbol(x3, Decl(unknownControlFlow.ts, 19, 7)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 19, 18)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 16, 13)) let x4: ThisOrThatNode | null | undefined = u; ->x4 : Symbol(x4, Decl(unknownControlFlow.ts, 16, 7)) ->ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 9, 19)) ->u : Symbol(u, Decl(unknownControlFlow.ts, 12, 13)) +>x4 : Symbol(x4, Decl(unknownControlFlow.ts, 20, 7)) +>ThisOrThatNode : Symbol(ThisOrThatNode, Decl(unknownControlFlow.ts, 13, 19)) +>u : Symbol(u, Decl(unknownControlFlow.ts, 16, 13)) } function f10(x: unknown) { ->f10 : Symbol(f10, Decl(unknownControlFlow.ts, 17, 1)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>f10 : Symbol(f10, Decl(unknownControlFlow.ts, 21, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) if (x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) x; // {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) } else { x; // unknown ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) } if (!x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) x; // unknown ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) } else { x; // {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 19, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 23, 13)) } } function f11(x: T) { ->f11 : Symbol(f11, Decl(unknownControlFlow.ts, 32, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 34, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 34, 13)) +>f11 : Symbol(f11, Decl(unknownControlFlow.ts, 36, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 38, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 38, 13)) if (x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) } if (!x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) } else { x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 34, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 38, 16)) } } function f12(x: T) { ->f12 : Symbol(f12, Decl(unknownControlFlow.ts, 47, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 49, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 49, 13)) +>f12 : Symbol(f12, Decl(unknownControlFlow.ts, 51, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 53, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 53, 27)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 53, 13)) if (x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 53, 27)) x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 53, 27)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 49, 27)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 53, 27)) } } function f20(x: unknown) { ->f20 : Symbol(f20, Decl(unknownControlFlow.ts, 56, 1)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>f20 : Symbol(f20, Decl(unknownControlFlow.ts, 60, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) if (x !== undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) >undefined : Symbol(undefined) x; // {} | null ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } else { x; // undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } if (x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) x; // {} | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } else { x; // null ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } if (x !== undefined && x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) >undefined : Symbol(undefined) ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) x; // {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } else { x; // null | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } if (x != undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) >undefined : Symbol(undefined) x; // {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } else { x; // null | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } if (x != null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) x; // {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } else { x; // null | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 58, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 62, 13)) } } function f21(x: T) { ->f21 : Symbol(f21, Decl(unknownControlFlow.ts, 89, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 91, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 91, 13)) +>f21 : Symbol(f21, Decl(unknownControlFlow.ts, 93, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 95, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 95, 13)) if (x !== undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) >undefined : Symbol(undefined) x; // T & ({} | null) ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } if (x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) x; // T & ({} | undefined) ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } if (x !== undefined && x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) >undefined : Symbol(undefined) ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } if (x != undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) >undefined : Symbol(undefined) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } if (x != null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 91, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 95, 16)) } } function f22(x: T) { ->f22 : Symbol(f22, Decl(unknownControlFlow.ts, 122, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 124, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 124, 13)) +>f22 : Symbol(f22, Decl(unknownControlFlow.ts, 126, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 128, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 128, 13)) if (x !== undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) >undefined : Symbol(undefined) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } if (x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } if (x !== undefined && x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) >undefined : Symbol(undefined) ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } if (x != undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) >undefined : Symbol(undefined) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } if (x != null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } else { x; // T ->x : Symbol(x, Decl(unknownControlFlow.ts, 124, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 128, 39)) } } function f23(x: T | undefined | null) { ->f23 : Symbol(f23, Decl(unknownControlFlow.ts, 155, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 157, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 157, 13)) +>f23 : Symbol(f23, Decl(unknownControlFlow.ts, 159, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 161, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 161, 13)) if (x !== undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) >undefined : Symbol(undefined) x; // T & {} | null ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) } if (x !== null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) x; // T & {} | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) } if (x != undefined) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) >undefined : Symbol(undefined) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) } if (x != null) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) x; // T & {} ->x : Symbol(x, Decl(unknownControlFlow.ts, 157, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 161, 16)) } } function f30(x: {}) { ->f30 : Symbol(f30, Decl(unknownControlFlow.ts, 170, 1)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) +>f30 : Symbol(f30, Decl(unknownControlFlow.ts, 174, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 176, 13)) if (typeof x === "object") { ->x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 176, 13)) x; // object ->x : Symbol(x, Decl(unknownControlFlow.ts, 172, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 176, 13)) } } function f31(x: T) { ->f31 : Symbol(f31, Decl(unknownControlFlow.ts, 176, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 178, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 178, 13)) +>f31 : Symbol(f31, Decl(unknownControlFlow.ts, 180, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 182, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 182, 13)) if (typeof x === "object") { ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) x; // T & object | T & null ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) } if (x && typeof x === "object") { ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) x; // T & object ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) } if (typeof x === "object" && x) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) x; // T & object ->x : Symbol(x, Decl(unknownControlFlow.ts, 178, 16)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 182, 16)) } } function f32(x: T) { ->f32 : Symbol(f32, Decl(unknownControlFlow.ts, 188, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 190, 13)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 190, 13)) +>f32 : Symbol(f32, Decl(unknownControlFlow.ts, 192, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 194, 13)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 194, 39)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 194, 13)) if (typeof x === "object") { ->x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 194, 39)) x; // T & object ->x : Symbol(x, Decl(unknownControlFlow.ts, 190, 39)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 194, 39)) } } function possiblyNull(x: T) { ->possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 196, 22)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 196, 25)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 196, 22)) +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 198, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 200, 22)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 200, 25)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 200, 22)) return !!true ? x : null; // T | null ->x : Symbol(x, Decl(unknownControlFlow.ts, 196, 25)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 200, 25)) } function possiblyUndefined(x: T) { ->possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 200, 27)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 200, 30)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 200, 27)) +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 204, 27)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 204, 30)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 204, 27)) return !!true ? x : undefined; // T | undefined ->x : Symbol(x, Decl(unknownControlFlow.ts, 200, 30)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 204, 30)) >undefined : Symbol(undefined) } function possiblyNullOrUndefined(x: T) { ->possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 204, 33)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 204, 36)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 204, 33)) +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 206, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 208, 33)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 208, 36)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 208, 33)) return possiblyUndefined(possiblyNull(x)); // T | null | undefined ->possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) ->possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 204, 36)) +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 198, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 208, 36)) } function ensureNotNull(x: T) { ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 208, 23)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 208, 23)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 212, 23)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 212, 26)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 212, 23)) if (x === null) throw Error(); ->x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 212, 26)) >Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) return x; // T & ({} | undefined) ->x : Symbol(x, Decl(unknownControlFlow.ts, 208, 26)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 212, 26)) } function ensureNotUndefined(x: T) { ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 213, 28)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 213, 28)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 217, 28)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 217, 31)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 217, 28)) if (x === undefined) throw Error(); ->x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 217, 31)) >undefined : Symbol(undefined) >Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) return x; // T & ({} | null) ->x : Symbol(x, Decl(unknownControlFlow.ts, 213, 31)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 217, 31)) } function ensureNotNullOrUndefined(x: T) { ->ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 218, 34)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 218, 37)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 218, 34)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 220, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 222, 34)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 222, 37)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 222, 34)) return ensureNotUndefined(ensureNotNull(x)); // T & {} ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 218, 37)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 222, 37)) } function f40(a: string | undefined, b: number | null | undefined) { ->f40 : Symbol(f40, Decl(unknownControlFlow.ts, 220, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 222, 13)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 222, 35)) +>f40 : Symbol(f40, Decl(unknownControlFlow.ts, 224, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 226, 13)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 226, 35)) let a1 = ensureNotNullOrUndefined(a); // string ->a1 : Symbol(a1, Decl(unknownControlFlow.ts, 223, 7)) ->ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 222, 13)) +>a1 : Symbol(a1, Decl(unknownControlFlow.ts, 227, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 220, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 226, 13)) let b1 = ensureNotNullOrUndefined(b); // number ->b1 : Symbol(b1, Decl(unknownControlFlow.ts, 224, 7)) ->ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 222, 35)) +>b1 : Symbol(b1, Decl(unknownControlFlow.ts, 228, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 220, 1)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 226, 35)) } type QQ = NonNullable>>; ->QQ : Symbol(QQ, Decl(unknownControlFlow.ts, 225, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 227, 8)) +>QQ : Symbol(QQ, Decl(unknownControlFlow.ts, 229, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 231, 8)) >NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) >NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) >NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 227, 8)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 231, 8)) function f41(a: T) { ->f41 : Symbol(f41, Decl(unknownControlFlow.ts, 227, 54)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 229, 13)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 229, 13)) +>f41 : Symbol(f41, Decl(unknownControlFlow.ts, 231, 54)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 233, 13)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 233, 13)) let a1 = ensureNotUndefined(ensureNotNull(a)); // T & {} ->a1 : Symbol(a1, Decl(unknownControlFlow.ts, 230, 7)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a1 : Symbol(a1, Decl(unknownControlFlow.ts, 234, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a2 = ensureNotNull(ensureNotUndefined(a)); // T & {} ->a2 : Symbol(a2, Decl(unknownControlFlow.ts, 231, 7)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a2 : Symbol(a2, Decl(unknownControlFlow.ts, 235, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a3 = ensureNotNull(ensureNotNull(a)); // T & {} | T & undefined ->a3 : Symbol(a3, Decl(unknownControlFlow.ts, 232, 7)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a3 : Symbol(a3, Decl(unknownControlFlow.ts, 236, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a4 = ensureNotUndefined(ensureNotUndefined(a)); // T & {} | T & null ->a4 : Symbol(a4, Decl(unknownControlFlow.ts, 233, 7)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a4 : Symbol(a4, Decl(unknownControlFlow.ts, 237, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a5 = ensureNotNullOrUndefined(ensureNotNullOrUndefined(a)); // T & {} ->a5 : Symbol(a5, Decl(unknownControlFlow.ts, 234, 7)) ->ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) ->ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 216, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a5 : Symbol(a5, Decl(unknownControlFlow.ts, 238, 7)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 220, 1)) +>ensureNotNullOrUndefined : Symbol(ensureNotNullOrUndefined, Decl(unknownControlFlow.ts, 220, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a6 = ensureNotNull(possiblyNullOrUndefined(a)); // T & {} | undefined ->a6 : Symbol(a6, Decl(unknownControlFlow.ts, 235, 7)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a6 : Symbol(a6, Decl(unknownControlFlow.ts, 239, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 206, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a7 = ensureNotUndefined(possiblyNullOrUndefined(a)); // T & {} | null ->a7 : Symbol(a7, Decl(unknownControlFlow.ts, 236, 7)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 202, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a7 : Symbol(a7, Decl(unknownControlFlow.ts, 240, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>possiblyNullOrUndefined : Symbol(possiblyNullOrUndefined, Decl(unknownControlFlow.ts, 206, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a8 = ensureNotNull(possiblyUndefined(a)); // T & {} | undefined ->a8 : Symbol(a8, Decl(unknownControlFlow.ts, 237, 7)) ->ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 206, 1)) ->possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 198, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a8 : Symbol(a8, Decl(unknownControlFlow.ts, 241, 7)) +>ensureNotNull : Symbol(ensureNotNull, Decl(unknownControlFlow.ts, 210, 1)) +>possiblyUndefined : Symbol(possiblyUndefined, Decl(unknownControlFlow.ts, 202, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) let a9 = ensureNotUndefined(possiblyNull(a)); // T & {} | null ->a9 : Symbol(a9, Decl(unknownControlFlow.ts, 238, 7)) ->ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 211, 1)) ->possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 194, 1)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 229, 16)) +>a9 : Symbol(a9, Decl(unknownControlFlow.ts, 242, 7)) +>ensureNotUndefined : Symbol(ensureNotUndefined, Decl(unknownControlFlow.ts, 215, 1)) +>possiblyNull : Symbol(possiblyNull, Decl(unknownControlFlow.ts, 198, 1)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 233, 16)) } // Repro from #48468 function deepEquals(a: T, b: T): boolean { ->deepEquals : Symbol(deepEquals, Decl(unknownControlFlow.ts, 239, 1)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) ->T : Symbol(T, Decl(unknownControlFlow.ts, 243, 20)) +>deepEquals : Symbol(deepEquals, Decl(unknownControlFlow.ts, 243, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 247, 20)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 247, 23)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 247, 20)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 247, 28)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 247, 20)) if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { ->a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 247, 23)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 247, 28)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 247, 23)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 247, 28)) return false; } @@ -580,11 +589,11 @@ function deepEquals(a: T, b: T): boolean { >Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) >Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 247, 23)) >Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) >Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 247, 28)) return false; } @@ -593,13 +602,13 @@ function deepEquals(a: T, b: T): boolean { >Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) ->a : Symbol(a, Decl(unknownControlFlow.ts, 243, 23)) +>a : Symbol(a, Decl(unknownControlFlow.ts, 247, 23)) >length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) >Object.keys(b).length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) >Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --)) ->b : Symbol(b, Decl(unknownControlFlow.ts, 243, 28)) +>b : Symbol(b, Decl(unknownControlFlow.ts, 247, 28)) >length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) return false; diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index 13610de77868f..b69c100b1e895 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -22,6 +22,15 @@ type T06 = {} & undefined; // never type T07 = undefined & void; // undefined >T07 : undefined +type T10 = string & {}; // Specially preserved +>T10 : string & {} + +type T11 = number & {}; // Specially preserved +>T11 : number & {} + +type T12 = bigint & {}; // Specially preserved +>T12 : bigint & {} + type ThisNode = {}; >ThisNode : {} diff --git a/tests/cases/conformance/types/unknown/unknownControlFlow.ts b/tests/cases/conformance/types/unknown/unknownControlFlow.ts index 89c677b6d1eb1..92a0f279e5eca 100644 --- a/tests/cases/conformance/types/unknown/unknownControlFlow.ts +++ b/tests/cases/conformance/types/unknown/unknownControlFlow.ts @@ -9,6 +9,10 @@ type T05 = {} & null; // never type T06 = {} & undefined; // never type T07 = undefined & void; // undefined +type T10 = string & {}; // Specially preserved +type T11 = number & {}; // Specially preserved +type T12 = bigint & {}; // Specially preserved + type ThisNode = {}; type ThatNode = {}; type ThisOrThatNode = ThisNode | ThatNode; From e70bf7c0dae6ef8ed2641c581fcac89656bdd07f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 12:20:46 -0700 Subject: [PATCH 19/30] Fix getNormalizedType and getNarrowableTypeForReference for intersections --- src/compiler/checker.ts | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b5246b2fae2f9..a16e4f62e3858 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18261,16 +18261,28 @@ namespace ts { function getNormalizedType(type: Type, writing: boolean): Type { while (true) { - let t = isFreshLiteralType(type) ? (type as FreshableType).regularType : - getObjectFlags(type) & ObjectFlags.Reference && (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : - type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) : + const t = isFreshLiteralType(type) ? (type as FreshableType).regularType : + getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : getSingleBaseForNonAugmentingSubtype(type) || type : + type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) : type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : (type as SubstitutionType).substitute : type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : type; - t = getSingleBaseForNonAugmentingSubtype(t) || t; - if (t === type) break; + if (t === type) return t; type = t; } + } + + function getNormalizedUnionOrIntersectionType(type: UnionOrIntersectionType, writing: boolean) { + const reduced = getReducedType(type); + if (reduced !== type) { + return reduced; + } + if (type.flags & TypeFlags.Intersection) { + const normalizedTypes = sameMap(type.types, t => getNormalizedType(t, writing)); + if (normalizedTypes !== type.types) { + return getIntersectionType(normalizedTypes); + } + } return type; } @@ -21422,10 +21434,10 @@ namespace ts { if (!deferredGlobalNonNullableTypeAlias) { deferredGlobalNonNullableTypeAlias = getGlobalSymbol("NonNullable" as __String, SymbolFlags.TypeAlias, /*diagnostic*/ undefined) || unknownSymbol; } - // If the NonNullable type is available, return an instantiation. Otherwise just return the reduced type. - return deferredGlobalNonNullableTypeAlias !== unknownSymbol ? - getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [reducedType]) : - reducedType; + if (deferredGlobalNonNullableTypeAlias === unknownSymbol || !maybeTypeOfKind(reducedType, TypeFlags.Instantiable | TypeFlags.Intersection)) { + return reducedType; + } + return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [reducedType]); } function getNonNullableType(type: Type): Type { @@ -25485,8 +25497,10 @@ namespace ts { !(someType(type, isGenericTypeWithoutNullableConstraint) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression))); } - function isGenericTypeWithUnionConstraint(type: Type) { - return !!(type.flags & TypeFlags.Instantiable && getBaseConstraintOrType(type).flags & (TypeFlags.Nullable | TypeFlags.Union)); + function isGenericTypeWithUnionConstraint(type: Type): boolean { + return type.flags & TypeFlags.Intersection ? + some((type as IntersectionType).types, isGenericTypeWithUnionConstraint) : + !!(type.flags & TypeFlags.Instantiable && getBaseConstraintOrType(type).flags & (TypeFlags.Nullable | TypeFlags.Union)); } function isGenericTypeWithoutNullableConstraint(type: Type) { @@ -25517,7 +25531,7 @@ namespace ts { const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) && someType(type, isGenericTypeWithUnionConstraint) && (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode)); - return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type; + return substituteConstraints ? mapType(type, getBaseConstraintOrType) : type; } function isExportOrExportExpression(location: Node) { From 7a62983572199f3abd70a755ad284a714da39f50 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 12:21:08 -0700 Subject: [PATCH 20/30] Switch NonNullable to use T & {} --- src/lib/es5.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 75b9539cc7592..754f6655db1a6 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1564,7 +1564,7 @@ type Omit = Pick>; /** * Exclude null and undefined from T */ -type NonNullable = T extends null | undefined ? never : T; +type NonNullable = T & {}; /** * Obtain the parameters of a function type in a tuple From c35cba77ab7e1ca6c5eb503468af2cca56dc310a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 12:32:15 -0700 Subject: [PATCH 21/30] Accept new baselines --- .../reference/conditionalTypes1.errors.txt | 29 +++++++++++--- .../reference/conditionalTypes1.types | 4 +- .../reference/conditionalTypes2.errors.txt | 5 +-- .../reference/controlFlowGenericTypes.types | 12 +++--- .../reference/intersectionReduction.types | 4 +- ...intersectionWithUnionConstraint.errors.txt | 30 ++++++++++----- .../intersectionWithUnionConstraint.types | 20 +++++----- .../reference/mappedTypes6.errors.txt | 13 +------ .../propTypeValidatorInference.types | 38 +++++++++---------- .../recursiveTupleTypeInference.errors.txt | 8 ++-- .../reference/unionWithIndexSignature.types | 2 +- 11 files changed, 90 insertions(+), 75 deletions(-) diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt index 85f50cb0e7d05..3c4c5340793b5 100644 --- a/tests/baselines/reference/conditionalTypes1.errors.txt +++ b/tests/baselines/reference/conditionalTypes1.errors.txt @@ -1,12 +1,21 @@ -tests/cases/conformance/types/conditional/conditionalTypes1.ts(12,5): error TS2322: Type 'T' is not assignable to type 'NonNullable'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(17,5): error TS2322: Type 'T' is not assignable to type 'NonNullable'. Type 'string | undefined' is not assignable to type 'NonNullable'. Type 'undefined' is not assignable to type 'NonNullable'. + Type 'undefined' is not assignable to type 'T'. + 'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'. + Type 'T' is not assignable to type '{}'. + Type 'string | undefined' is not assignable to type '{}'. + Type 'undefined' is not assignable to type '{}'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(24,5): error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'NonNullable[keyof T]>'. - Type 'undefined' is not assignable to type 'NonNullable[keyof T]>'. + Type 'undefined' is not assignable to type 'T[keyof T] & {}'. + Type 'undefined' is not assignable to type 'T[keyof T]'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(29,5): error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable'. Type 'string | undefined' is not assignable to type 'NonNullable'. Type 'undefined' is not assignable to type 'NonNullable'. + Type 'undefined' is not assignable to type '{}'. + Type 'T["x"]' is not assignable to type '{}'. + Type 'string | undefined' is not assignable to type '{}'. + Type 'undefined' is not assignable to type '{}'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(103,5): error TS2322: Type 'FunctionProperties' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'FunctionProperties'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2322: Type 'NonFunctionProperties' is not assignable to type 'T'. @@ -57,7 +66,7 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS Type 'boolean' is not assignable to type 'true'. -==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (20 errors) ==== +==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (19 errors) ==== type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" @@ -70,8 +79,6 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS function f1(x: T, y: NonNullable) { x = y; y = x; // Error - ~ -!!! error TS2322: Type 'T' is not assignable to type 'NonNullable'. } function f2(x: T, y: NonNullable) { @@ -81,6 +88,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS !!! error TS2322: Type 'T' is not assignable to type 'NonNullable'. !!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable'. !!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T'. +!!! error TS2322: 'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'. +!!! error TS2322: Type 'T' is not assignable to type '{}'. +!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'. +!!! error TS2322: Type 'undefined' is not assignable to type '{}'. let s1: string = x; // Error let s2: string = y; } @@ -90,7 +102,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS y = x; // Error ~ !!! error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'NonNullable[keyof T]>'. -!!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable[keyof T]>'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T] & {}'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T]'. } function f4(x: T["x"], y: NonNullable) { @@ -100,6 +113,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS !!! error TS2322: Type 'T["x"]' is not assignable to type 'NonNullable'. !!! error TS2322: Type 'string | undefined' is not assignable to type 'NonNullable'. !!! error TS2322: Type 'undefined' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'undefined' is not assignable to type '{}'. +!!! error TS2322: Type 'T["x"]' is not assignable to type '{}'. +!!! error TS2322: Type 'string | undefined' is not assignable to type '{}'. +!!! error TS2322: Type 'undefined' is not assignable to type '{}'. let s1: string = x; // Error let s2: string = y; } diff --git a/tests/baselines/reference/conditionalTypes1.types b/tests/baselines/reference/conditionalTypes1.types index 27b295caa8b50..ec81f4b151f3b 100644 --- a/tests/baselines/reference/conditionalTypes1.types +++ b/tests/baselines/reference/conditionalTypes1.types @@ -55,7 +55,7 @@ function f2(x: T, y: NonNullable) { let s2: string = y; >s2 : string ->y : NonNullable +>y : string } function f3(x: Partial[keyof T], y: NonNullable[keyof T]>) { @@ -96,7 +96,7 @@ function f4(x: T["x"], y: NonNullables2 : string ->y : NonNullable +>y : string } type Options = { k: "a", a: number } | { k: "b", b: string } | { k: "c", c: boolean }; diff --git a/tests/baselines/reference/conditionalTypes2.errors.txt b/tests/baselines/reference/conditionalTypes2.errors.txt index 7a4f8bf73447f..673156cf50e0b 100644 --- a/tests/baselines/reference/conditionalTypes2.errors.txt +++ b/tests/baselines/reference/conditionalTypes2.errors.txt @@ -32,7 +32,7 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(74,12): error TS2 Property 'bat' is missing in type 'Foo & Bar' but required in type '{ foo: string; bat: string; }'. tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2345: Argument of type 'Extract2' is not assignable to parameter of type '{ foo: string; bat: string; }'. Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'. - Property 'bat' is missing in type 'Bar & Foo' but required in type '{ foo: string; bat: string; }'. + Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'. ==== tests/cases/conformance/types/conditional/conditionalTypes2.ts (7 errors) ==== @@ -155,8 +155,7 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2 ~ !!! error TS2345: Argument of type 'Extract2' is not assignable to parameter of type '{ foo: string; bat: string; }'. !!! error TS2345: Type 'T extends Bar ? T : never' is not assignable to type '{ foo: string; bat: string; }'. -!!! error TS2345: Property 'bat' is missing in type 'Bar & Foo' but required in type '{ foo: string; bat: string; }'. -!!! related TS2728 tests/cases/conformance/types/conditional/conditionalTypes2.ts:62:43: 'bat' is declared here. +!!! error TS2345: Type 'Bar & Foo & T' is not assignable to type '{ foo: string; bat: string; }'. } // Repros from #22860 diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index 7916b0f60e034..98cf0f3276c35 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -44,13 +44,13 @@ function f1(x: T, y: { a: T }, z: [T]): string { z[0].length; >z[0].length : number ->z[0] : T & {} +>z[0] : string >z : [T] >0 : 0 >length : number return z[0]; ->z[0] : T & {} +>z[0] : string >z : [T] >0 : 0 } @@ -438,8 +438,8 @@ function fx3 | undefined, K extends keyof T>(o >key : K const x1 = obj[key]; // Error ->x1 : NonNullable>[K] ->obj[key] : NonNullable>[K] +>x1 : Record[K] +>obj[key] : Record[K] >obj : Record | undefined >key : K @@ -469,14 +469,14 @@ class TableBaseEnum< >null : null iSpec[null! as keyof InternalSpec]; // Error, object possibly undefined ->iSpec[null! as keyof InternalSpec] : NonNullable>[keyof InternalSpec] +>iSpec[null! as keyof InternalSpec] : Record[keyof InternalSpec] >iSpec : Record | undefined >null! as keyof InternalSpec : keyof InternalSpec >null! : never >null : null iSpec[null! as keyof PublicSpec]; // Error, object possibly undefined ->iSpec[null! as keyof PublicSpec] : NonNullable>[keyof PublicSpec] +>iSpec[null! as keyof PublicSpec] : Record[keyof PublicSpec] >iSpec : Record | undefined >null! as keyof PublicSpec : keyof PublicSpec >null! : never diff --git a/tests/baselines/reference/intersectionReduction.types b/tests/baselines/reference/intersectionReduction.types index 4ecfe50e4271b..fc04d01441c43 100644 --- a/tests/baselines/reference/intersectionReduction.types +++ b/tests/baselines/reference/intersectionReduction.types @@ -346,7 +346,7 @@ function inGeneric(alsoShouldBeB: T & B2) { const b: B2 = alsoShouldBeB; >b : B2 ->alsoShouldBeB : T & B2 +>alsoShouldBeB : B2 } // Repro from #38542 @@ -374,6 +374,6 @@ function bar(x: T & CA) { let ab: ABI = x; >ab : ABI ->x : T & CA +>x : CA } diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt index 0dd473a4f4a14..2a4ee7b3a3758 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt +++ b/tests/baselines/reference/intersectionWithUnionConstraint.errors.txt @@ -1,8 +1,13 @@ -tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(7,9): error TS2322: Type 'T & U' is not assignable to type 'string | number'. -tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(8,9): error TS2322: Type 'T & U' is not assignable to type 'string | null'. -tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(10,9): error TS2322: Type 'T & U' is not assignable to type 'number | null'. -tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(11,9): error TS2322: Type 'T & U' is not assignable to type 'number | undefined'. -tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12,9): error TS2322: Type 'T & U' is not assignable to type 'null | undefined'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(7,9): error TS2322: Type 'string | undefined' is not assignable to type 'string | number'. + Type 'undefined' is not assignable to type 'string | number'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(8,9): error TS2322: Type 'string | undefined' is not assignable to type 'string | null'. + Type 'undefined' is not assignable to type 'string | null'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(10,9): error TS2322: Type 'string | undefined' is not assignable to type 'number | null'. + Type 'undefined' is not assignable to type 'number | null'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(11,9): error TS2322: Type 'string | undefined' is not assignable to type 'number | undefined'. + Type 'string' is not assignable to type 'number'. +tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12,9): error TS2322: Type 'string | undefined' is not assignable to type 'null | undefined'. + Type 'string' is not assignable to type 'null | undefined'. ==== tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts (5 errors) ==== @@ -14,20 +19,25 @@ tests/cases/conformance/types/intersection/intersectionWithUnionConstraint.ts(12 function f2(x: T & U) { let y1: string | number = x; // Error ~~ -!!! error TS2322: Type 'T & U' is not assignable to type 'string | number'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'. let y2: string | null = x; // Error ~~ -!!! error TS2322: Type 'T & U' is not assignable to type 'string | null'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string | null'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string | null'. let y3: string | undefined = x; let y4: number | null = x; // Error ~~ -!!! error TS2322: Type 'T & U' is not assignable to type 'number | null'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | null'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number | null'. let y5: number | undefined = x; // Error ~~ -!!! error TS2322: Type 'T & U' is not assignable to type 'number | undefined'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'number | undefined'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. let y6: null | undefined = x; // Error ~~ -!!! error TS2322: Type 'T & U' is not assignable to type 'null | undefined'. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'null | undefined'. +!!! error TS2322: Type 'string' is not assignable to type 'null | undefined'. } type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.types b/tests/baselines/reference/intersectionWithUnionConstraint.types index ba079e2bf81aa..5b530f76f46b6 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.types +++ b/tests/baselines/reference/intersectionWithUnionConstraint.types @@ -6,7 +6,7 @@ function f1(x: T & U) { // Combined constraint of 'T & U' is 'string | number' let y: string | number = x; >y : string | number ->x : T & U +>x : string | number } function f2(x: T & U) { @@ -16,30 +16,30 @@ function f2y1 : string | number ->x : T & U +>x : string | undefined let y2: string | null = x; // Error >y2 : string | null >null : null ->x : T & U +>x : string | undefined let y3: string | undefined = x; >y3 : string | undefined ->x : T & U +>x : string | undefined let y4: number | null = x; // Error >y4 : number | null >null : null ->x : T & U +>x : string | undefined let y5: number | undefined = x; // Error >y5 : number | undefined ->x : T & U +>x : string | undefined let y6: null | undefined = x; // Error >y6 : null | undefined >null : null ->x : T & U +>x : string | undefined } type T1 = (string | number | undefined) & (string | null | undefined); // string | undefined @@ -52,7 +52,7 @@ function f3(x: T & (number | object | und const y: number | undefined = x; >y : number | undefined ->x : T & (number | object | undefined) +>x : number | undefined } function f4(x: T & (number | object)) { @@ -61,7 +61,7 @@ function f4(x: T & (number | object)) { const y: number = x; >y : number ->x : T & (number | object) +>x : number } function f5(x: keyof T & U) { @@ -70,7 +70,7 @@ function f5(x: keyof T & U) { let y: keyof any = x; >y : string | number | symbol ->x : keyof T & U +>x : string | number | symbol } // Repro from #23648 diff --git a/tests/baselines/reference/mappedTypes6.errors.txt b/tests/baselines/reference/mappedTypes6.errors.txt index 2429ea8558f10..204a0b8e68393 100644 --- a/tests/baselines/reference/mappedTypes6.errors.txt +++ b/tests/baselines/reference/mappedTypes6.errors.txt @@ -2,11 +2,6 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(23,5): error TS2322: Type ' tests/cases/conformance/types/mapped/mappedTypes6.ts(24,5): error TS2322: Type 'Partial' is not assignable to type 'Required'. tests/cases/conformance/types/mapped/mappedTypes6.ts(27,5): error TS2322: Type 'Partial' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Partial'. -tests/cases/conformance/types/mapped/mappedTypes6.ts(37,5): error TS2322: Type 'Required' is not assignable to type 'Denullified'. - Type 'T[P]' is not assignable to type 'NonNullable'. - Type 'T[keyof T]' is not assignable to type 'NonNullable'. - Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'NonNullable'. - Type 'T[string]' is not assignable to type 'NonNullable'. tests/cases/conformance/types/mapped/mappedTypes6.ts(38,5): error TS2322: Type 'T' is not assignable to type 'Denullified'. tests/cases/conformance/types/mapped/mappedTypes6.ts(39,5): error TS2322: Type 'Partial' is not assignable to type 'Denullified'. tests/cases/conformance/types/mapped/mappedTypes6.ts(42,5): error TS2322: Type 'T' is not assignable to type 'Required'. @@ -26,7 +21,7 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(119,4): error TS2540: Canno tests/cases/conformance/types/mapped/mappedTypes6.ts(120,4): error TS2540: Cannot assign to 'b' because it is a read-only property. -==== tests/cases/conformance/types/mapped/mappedTypes6.ts (19 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypes6.ts (18 errors) ==== type T00 = { [P in keyof T]: T[P] }; type T01 = { [P in keyof T]?: T[P] }; type T02 = { [P in keyof T]+?: T[P] }; @@ -71,12 +66,6 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(120,4): error TS2540: Canno function f2(w: Denullified, x: Required, y: T, z: Partial) { w = w; w = x; // Error - ~ -!!! error TS2322: Type 'Required' is not assignable to type 'Denullified'. -!!! error TS2322: Type 'T[P]' is not assignable to type 'NonNullable'. -!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'NonNullable'. -!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'NonNullable'. -!!! error TS2322: Type 'T[string]' is not assignable to type 'NonNullable'. w = y; // Error ~ !!! error TS2322: Type 'T' is not assignable to type 'Denullified'. diff --git a/tests/baselines/reference/propTypeValidatorInference.types b/tests/baselines/reference/propTypeValidatorInference.types index ed705dbd73061..2a2feafc30316 100644 --- a/tests/baselines/reference/propTypeValidatorInference.types +++ b/tests/baselines/reference/propTypeValidatorInference.types @@ -179,7 +179,7 @@ const arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ // TS checking const propTypes: PropTypesMap = { >propTypes : PropTypes.ValidationMap ->{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } +>{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; } any: PropTypes.any, >any : PropTypes.Requireable @@ -204,31 +204,31 @@ const propTypes: PropTypesMap = { >isRequired : PropTypes.Validator shape: PropTypes.shape(innerProps).isRequired, ->shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> ->PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> +>PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> >PropTypes.shape(innerProps) : PropTypes.Requireable; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> >PropTypes.shape :

>(type: P) => PropTypes.Requireable> >PropTypes : typeof PropTypes >shape :

>(type: P) => PropTypes.Requireable> >innerProps : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } ->isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, ->oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>> ->PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> ->PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>> +>oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>>>> +>PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>>>> +>PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>>> >PropTypes.oneOfType : >(types: T[]) => PropTypes.Requireable>> >PropTypes : typeof PropTypes >oneOfType : >(types: T[]) => PropTypes.Requireable>> >arrayOfTypes : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] ->isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> +>isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>>>> }; // JS checking const propTypesWithoutAnnotation = { ->propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } ->{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } +>propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; } +>{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; } any: PropTypes.any, >any : PropTypes.Requireable @@ -253,24 +253,24 @@ const propTypesWithoutAnnotation = { >isRequired : PropTypes.Validator shape: PropTypes.shape(innerProps).isRequired, ->shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> ->PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> +>PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> >PropTypes.shape(innerProps) : PropTypes.Requireable; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> >PropTypes.shape :

>(type: P) => PropTypes.Requireable> >PropTypes : typeof PropTypes >shape :

{ + public props: Readonly

; + constructor(props: P) { + this.props = Object.freeze(props); + } + } + + interface Foo { + foo: string; + } + + declare function merge(obj1: T, obj2: U): T & U; + + class AnotherSampleClass extends SampleClass { + constructor(props: T) { + const foo: Foo = { foo: "bar" }; + super(merge(props, foo)); + } + + public brokenMethod() { + this.props.foo.concat; + } + } + new AnotherSampleClass({}); + + // Positive repro from #17166 + function f3>(t: T, k: K, tk: T[K]): void { + for (let key in t) { + key = k // ok, K ==> keyof T + t[key] = tk; // ok, T[K] ==> T[keyof T] + } + } + + // # 21185 + type Predicates = { + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] + } + + // Repros from #23592 + + type Example = { [K in keyof T]: T[K]["prop"] }; + type Result = Example<{ a: { prop: string }; b: { prop: number } }>; + + type Helper2 = { [K in keyof T]: Extract }; + type Example2 = { [K in keyof Helper2]: Helper2[K]["prop"] }; + type Result2 = Example2<{ 1: { prop: string }; 2: { prop: number } }>; + + // Repro from #23618 + + type DBBoolTable = { [k in K]: 0 | 1 } + enum Flag { + FLAG_1 = "flag_1", + FLAG_2 = "flag_2" + } + + type SimpleDBRecord = { staticField: number } & DBBoolTable + function getFlagsFromSimpleRecord(record: SimpleDBRecord, flags: Flag[]) { + return record[flags[0]]; + } + + type DynamicDBRecord = ({ dynamicField: number } | { dynamicField: string }) & DBBoolTable + function getFlagsFromDynamicRecord(record: DynamicDBRecord, flags: Flag[]) { + return record[flags[0]]; + } + + // Repro from #21368 + + interface I { + foo: string; + } + + declare function take(p: T): void; + + function fn(o: T, k: K) { + take<{} | null | undefined>(o[k]); + take(o[k]); + } + + // Repro from #23133 + + class Unbounded { + foo(x: T[keyof T]) { + let y: {} | undefined | null = x; + } + } + + // Repro from #23940 + + interface I7 { + x: any; + } + type Foo7 = T; + declare function f7(type: K): Foo7; + + // Repro from #21770 + + type Dict = { [key in T]: number }; + type DictDict = { [key in V]: Dict }; + + function ff1(dd: DictDict, k1: V, k2: T): number { + return dd[k1][k2]; + } + + function ff2(dd: DictDict, k1: V, k2: T): number { + const d: Dict = dd[k1]; + return d[k2]; + } + + // Repro from #26409 + + const cf1 = (t: T, k: K) => + { + const s: string = t[k]; + t.cool; + }; + + const cf2 = (t: T, k: K) => + { + const s: string = t[k]; + t.cool; + }; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeAsClauseRelationships.errors.txt b/tests/baselines/reference/mappedTypeAsClauseRelationships.errors.txt index 98b164a866a27..eee91fc97e37b 100644 --- a/tests/baselines/reference/mappedTypeAsClauseRelationships.errors.txt +++ b/tests/baselines/reference/mappedTypeAsClauseRelationships.errors.txt @@ -1,9 +1,10 @@ tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(12,9): error TS2322: Type 'T' is not assignable to type 'Modify'. +tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(22,9): error TS2322: Type 'T' is not assignable to type 'ModifyInclOpt'. tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(23,9): error TS2322: Type 'T' is not assignable to type 'FilterExclOpt'. tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(24,9): error TS2322: Type 'T' is not assignable to type 'ModifyExclOpt'. -==== tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts (3 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts (4 errors) ==== // From original issue #45212: type Methods = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; type H = T[keyof Methods]; // Ok @@ -28,6 +29,8 @@ tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(24,9): e function fun2(val: T) { let x: FilterInclOpt = val; // Ok let y: ModifyInclOpt = val; // Ok + ~ +!!! error TS2322: Type 'T' is not assignable to type 'ModifyInclOpt'. let z: FilterExclOpt = val; // Error ~ !!! error TS2322: Type 'T' is not assignable to type 'FilterExclOpt'. diff --git a/tests/baselines/reference/mappedTypes6.errors.txt b/tests/baselines/reference/mappedTypes6.errors.txt index 204a0b8e68393..d0476d61a705b 100644 --- a/tests/baselines/reference/mappedTypes6.errors.txt +++ b/tests/baselines/reference/mappedTypes6.errors.txt @@ -2,6 +2,25 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(23,5): error TS2322: Type ' tests/cases/conformance/types/mapped/mappedTypes6.ts(24,5): error TS2322: Type 'Partial' is not assignable to type 'Required'. tests/cases/conformance/types/mapped/mappedTypes6.ts(27,5): error TS2322: Type 'Partial' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Partial'. +tests/cases/conformance/types/mapped/mappedTypes6.ts(37,5): error TS2322: Type 'Required' is not assignable to type 'Denullified'. + Type 'T[P]' is not assignable to type 'NonNullable'. + Type 'T[keyof T]' is not assignable to type 'NonNullable'. + Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'NonNullable'. + Type 'T[string]' is not assignable to type 'NonNullable'. + Type 'T[string]' is not assignable to type 'T[P]'. + Type 'string' is not assignable to type 'P'. + 'string' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. + Type 'T[keyof T]' is not assignable to type 'T[P]'. + Type 'keyof T' is not assignable to type 'P'. + 'keyof T' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. + Type 'string | number | symbol' is not assignable to type 'P'. + 'string | number | symbol' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. + Type 'string' is not assignable to type 'P'. + 'string' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. + Type 'T[P]' is not assignable to type '{}'. + Type 'T[keyof T]' is not assignable to type '{}'. + Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'. + Type 'T[string]' is not assignable to type '{}'. tests/cases/conformance/types/mapped/mappedTypes6.ts(38,5): error TS2322: Type 'T' is not assignable to type 'Denullified'. tests/cases/conformance/types/mapped/mappedTypes6.ts(39,5): error TS2322: Type 'Partial' is not assignable to type 'Denullified'. tests/cases/conformance/types/mapped/mappedTypes6.ts(42,5): error TS2322: Type 'T' is not assignable to type 'Required'. @@ -21,7 +40,7 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(119,4): error TS2540: Canno tests/cases/conformance/types/mapped/mappedTypes6.ts(120,4): error TS2540: Cannot assign to 'b' because it is a read-only property. -==== tests/cases/conformance/types/mapped/mappedTypes6.ts (18 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypes6.ts (19 errors) ==== type T00 = { [P in keyof T]: T[P] }; type T01 = { [P in keyof T]?: T[P] }; type T02 = { [P in keyof T]+?: T[P] }; @@ -66,6 +85,26 @@ tests/cases/conformance/types/mapped/mappedTypes6.ts(120,4): error TS2540: Canno function f2(w: Denullified, x: Required, y: T, z: Partial) { w = w; w = x; // Error + ~ +!!! error TS2322: Type 'Required' is not assignable to type 'Denullified'. +!!! error TS2322: Type 'T[P]' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'T[P]'. +!!! error TS2322: Type 'string' is not assignable to type 'P'. +!!! error TS2322: 'string' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'T[P]'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'P'. +!!! error TS2322: 'keyof T' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. +!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'P'. +!!! error TS2322: 'string | number | symbol' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. +!!! error TS2322: Type 'string' is not assignable to type 'P'. +!!! error TS2322: 'string' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | symbol'. +!!! error TS2322: Type 'T[P]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[string]' is not assignable to type '{}'. w = y; // Error ~ !!! error TS2322: Type 'T' is not assignable to type 'Denullified'. diff --git a/tests/baselines/reference/mappedTypesAndObjects.errors.txt b/tests/baselines/reference/mappedTypesAndObjects.errors.txt new file mode 100644 index 0000000000000..c32fc58b370e7 --- /dev/null +++ b/tests/baselines/reference/mappedTypesAndObjects.errors.txt @@ -0,0 +1,54 @@ +tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts(25,11): error TS2430: Interface 'E1' incorrectly extends interface 'Base'. + Types of property 'foo' are incompatible. + Type 'T' is not assignable to type '{ [key: string]: any; }'. + + +==== tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts (1 errors) ==== + function f1(x: Partial, y: Readonly) { + let obj: {}; + obj = x; + obj = y; + } + + function f2(x: Partial, y: Readonly) { + let obj: { [x: string]: any }; + obj = x; + obj = y; + } + + function f3(x: Partial) { + x = {}; + } + + // Repro from #12900 + + interface Base { + foo: { [key: string]: any }; + bar: any; + baz: any; + } + + interface E1 extends Base { + ~~ +!!! error TS2430: Interface 'E1' incorrectly extends interface 'Base'. +!!! error TS2430: Types of property 'foo' are incompatible. +!!! error TS2430: Type 'T' is not assignable to type '{ [key: string]: any; }'. +!!! related TS2208 tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts:25:14: This type parameter probably needs an `extends object` constraint. + foo: T; + } + + interface Something { name: string, value: string }; + interface E2 extends Base { + foo: Partial; // or other mapped type + } + + interface E3 extends Base { + foo: Partial; // or other mapped type + } + + // Repro from #13747 + + class Form { + private values: {[P in keyof T]?: T[P]} = {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/tsxNotUsingApparentTypeOfSFC.errors.txt b/tests/baselines/reference/tsxNotUsingApparentTypeOfSFC.errors.txt index 81231a345627b..a967c92e555ac 100644 --- a/tests/baselines/reference/tsxNotUsingApparentTypeOfSFC.errors.txt +++ b/tests/baselines/reference/tsxNotUsingApparentTypeOfSFC.errors.txt @@ -5,9 +5,17 @@ tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(15,14): error TS2769: No o Type '{}' is not assignable to type 'Readonly

'. Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error. Type '{}' is not assignable to type 'Readonly

'. +tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(17,14): error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes & P'. + Type 'P' is not assignable to type 'IntrinsicAttributes'. +tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(18,14): error TS2769: No overload matches this call. + Overload 1 of 2, '(props: Readonly

): MyComponent', gave the following error. + Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly

'. + Type 'P' is not assignable to type 'IntrinsicAttributes'. + Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error. + Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly

'. -==== tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx (2 errors) ==== +==== tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx (4 errors) ==== /// import React from 'react'; @@ -34,5 +42,17 @@ tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx(15,14): error TS2769: No o !!! error TS2769: Type '{}' is not assignable to type 'Readonly

'. let z = // should work + ~~~~~ +!!! error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes & P'. +!!! error TS2322: Type 'P' is not assignable to type 'IntrinsicAttributes'. +!!! related TS2208 tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx:5:15: This type parameter probably needs an `extends object` constraint. let q = // should work + ~~~~~~~~~~~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(props: Readonly

): MyComponent', gave the following error. +!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly

'. +!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes'. +!!! error TS2769: Overload 2 of 2, '(props: P, context?: any): MyComponent', gave the following error. +!!! error TS2769: Type 'P' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly

'. +!!! related TS2208 tests/cases/compiler/tsxNotUsingApparentTypeOfSFC.tsx:5:15: This type parameter probably needs an `extends object` constraint. } \ No newline at end of file diff --git a/tests/baselines/reference/unknownType1.errors.txt b/tests/baselines/reference/unknownType1.errors.txt index 9aa098a781c53..a827753e5a25f 100644 --- a/tests/baselines/reference/unknownType1.errors.txt +++ b/tests/baselines/reference/unknownType1.errors.txt @@ -23,11 +23,12 @@ tests/cases/conformance/types/unknown/unknownType1.ts(144,29): error TS2698: Spr tests/cases/conformance/types/unknown/unknownType1.ts(150,17): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. tests/cases/conformance/types/unknown/unknownType1.ts(156,14): error TS2700: Rest types may only be created from object types. tests/cases/conformance/types/unknown/unknownType1.ts(162,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/conformance/types/unknown/unknownType1.ts(170,9): error TS2322: Type 'T' is not assignable to type '{}'. tests/cases/conformance/types/unknown/unknownType1.ts(171,9): error TS2322: Type 'U' is not assignable to type '{}'. tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type 'T' is not assignable to type '{}'. -==== tests/cases/conformance/types/unknown/unknownType1.ts (26 errors) ==== +==== tests/cases/conformance/types/unknown/unknownType1.ts (27 errors) ==== // In an intersection everything absorbs unknown type T00 = unknown & null; // null @@ -247,6 +248,9 @@ tests/cases/conformance/types/unknown/unknownType1.ts(181,5): error TS2322: Type function f30(t: T, u: U) { let x: {} = t; + ~ +!!! error TS2322: Type 'T' is not assignable to type '{}'. +!!! related TS2208 tests/cases/conformance/types/unknown/unknownType1.ts:169:14: This type parameter probably needs an `extends object` constraint. let y: {} = u; ~ !!! error TS2322: Type 'U' is not assignable to type '{}'.

>(type: P) => PropTypes.Requireable> >innerProps : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } ->isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>> oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, ->oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>> ->PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> ->PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>> +>oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>>>> +>PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>>>> +>PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>>> >PropTypes.oneOfType : >(types: T[]) => PropTypes.Requireable>> >PropTypes : typeof PropTypes >oneOfType : >(types: T[]) => PropTypes.Requireable>> >arrayOfTypes : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] ->isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> +>isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>>>> }; @@ -280,9 +280,9 @@ type ExtractedProps = PropTypes.InferProps; >propTypes : PropTypes.ValidationMap type ExtractedPropsWithoutAnnotation = PropTypes.InferProps; ->ExtractedPropsWithoutAnnotation : PropTypes.InferPropsInner; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; }, PropTypes.RequiredKeys<{ any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; }>>> & Partial; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; }, "any">>> +>ExtractedPropsWithoutAnnotation : PropTypes.InferPropsInner; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; }, PropTypes.RequiredKeys<{ any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; }>>> & Partial; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; }, "any">>> >PropTypes : any ->propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } +>propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>>>; } type ExtractPropsMatch = ExtractedProps extends ExtractedPropsWithoutAnnotation ? true : false; >ExtractPropsMatch : true diff --git a/tests/baselines/reference/recursiveTupleTypeInference.errors.txt b/tests/baselines/reference/recursiveTupleTypeInference.errors.txt index 53ed377d2b7ab..f9fbfce896a73 100644 --- a/tests/baselines/reference/recursiveTupleTypeInference.errors.txt +++ b/tests/baselines/reference/recursiveTupleTypeInference.errors.txt @@ -1,7 +1,7 @@ tests/cases/compiler/recursiveTupleTypeInference.ts(23,5): error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'. Types of property 'b' are incompatible. - Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. - Type 'string' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. + Type 'A' is not assignable to type '[never, "null"]'. + Type 'string' is not assignable to type '[never, "null"]'. ==== tests/cases/compiler/recursiveTupleTypeInference.ts (1 errors) ==== @@ -31,6 +31,6 @@ tests/cases/compiler/recursiveTupleTypeInference.ts(23,5): error TS2345: Argumen ~~ !!! error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'. !!! error TS2345: Types of property 'b' are incompatible. -!!! error TS2345: Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. -!!! error TS2345: Type 'string' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. +!!! error TS2345: Type 'A' is not assignable to type '[never, "null"]'. +!!! error TS2345: Type 'string' is not assignable to type '[never, "null"]'. \ No newline at end of file diff --git a/tests/baselines/reference/unionWithIndexSignature.types b/tests/baselines/reference/unionWithIndexSignature.types index 5acb5391f814f..7aa5877e37a25 100644 --- a/tests/baselines/reference/unionWithIndexSignature.types +++ b/tests/baselines/reference/unionWithIndexSignature.types @@ -21,7 +21,7 @@ export function foo(arr: T & (NumList | StrList)) { let zz = arr[1]; // Error >zz : string | number >arr[1] : string | number ->arr : T & (NumList | StrList) +>arr : NumList | StrList >1 : 1 } From a0c09292c8018fc50c6c7abc21ffa6853862a3ab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 13:49:35 -0700 Subject: [PATCH 22/30] Use NonNullable in place of anonymous T & {} --- src/compiler/checker.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a16e4f62e3858..48cf213041b5d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21427,21 +21427,23 @@ namespace ts { } function getGlobalNonNullableTypeInstantiation(type: Type) { - // First reduce away any constituents that are assignable to 'undefined' or 'null'. This not only eliminates - // 'undefined' and 'null', but also higher-order types such as a type parameter 'U extends undefined | null' - // that isn't eliminated by a NonNullable instantiation. - const reducedType = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); if (!deferredGlobalNonNullableTypeAlias) { deferredGlobalNonNullableTypeAlias = getGlobalSymbol("NonNullable" as __String, SymbolFlags.TypeAlias, /*diagnostic*/ undefined) || unknownSymbol; } - if (deferredGlobalNonNullableTypeAlias === unknownSymbol || !maybeTypeOfKind(reducedType, TypeFlags.Instantiable | TypeFlags.Intersection)) { - return reducedType; - } - return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [reducedType]); + return deferredGlobalNonNullableTypeAlias !== unknownSymbol ? + getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [type]) : + getIntersectionType([type, emptyObjectType]); } function getNonNullableType(type: Type): Type { - return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type; + if (strictNullChecks) { + // First reduce away any constituents that are assignable to 'undefined' or 'null'. This not only eliminates + // 'undefined' and 'null', but also higher-order types such as a type parameter 'U extends undefined | null' + // that isn't eliminated by a NonNullable instantiation. + const reducedType = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + return maybeTypeOfKind(reducedType, TypeFlags.Instantiable) ? getGlobalNonNullableTypeInstantiation(reducedType) : reducedType; + } + return type; } function addOptionalTypeMarker(type: Type) { @@ -23555,7 +23557,7 @@ namespace ts { return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined && !maybeTypeOfKind(reduced, TypeFlags.Undefined) ? getUnionType([emptyObjectType, undefinedType]) : emptyObjectType]): t); case TypeFacts.NEUndefinedOrNull: case TypeFacts.Truthy: - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getIntersectionType([t, emptyObjectType]): t); + return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getGlobalNonNullableTypeInstantiation(t): t); } } return reduced; From eff3be3496578f8304297d053ab3e3211505cc65 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 20 May 2022 13:49:56 -0700 Subject: [PATCH 23/30] Accept new baselines --- .../reference/controlFlowGenericTypes.types | 12 ++++++------ .../reference/controlFlowTruthiness.types | 2 +- .../strictNullNotNullIndexTypeNoLib.errors.txt | 5 +---- .../strictNullNotNullIndexTypeNoLib.symbols | 2 ++ .../strictNullNotNullIndexTypeNoLib.types | 10 +++++----- .../reference/typeVariableTypeGuards.types | 4 ++-- .../reference/unknownControlFlow.types | 18 +++++++++--------- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types index 98cf0f3276c35..e6ebd4226b700 100644 --- a/tests/baselines/reference/controlFlowGenericTypes.types +++ b/tests/baselines/reference/controlFlowGenericTypes.types @@ -10,7 +10,7 @@ function f1(x: T, y: { a: T }, z: [T]): string { >x : T x; ->x : T & {} +>x : NonNullable x.length; >x.length : number @@ -67,7 +67,7 @@ function f2(x: Extract | null): string { >x : Extract | null x; ->x : Extract & {} +>x : NonNullable> x.length; >x.length : number @@ -404,11 +404,11 @@ function fx1(obj: T, key: K) { >key : K const x2 = obj && obj[key]; ->x2 : (T & {})[K] ->obj && obj[key] : (T & {})[K] +>x2 : NonNullable[K] +>obj && obj[key] : NonNullable[K] >obj : T ->obj[key] : (T & {})[K] ->obj : T & {} +>obj[key] : NonNullable[K] +>obj : NonNullable >key : K } diff --git a/tests/baselines/reference/controlFlowTruthiness.types b/tests/baselines/reference/controlFlowTruthiness.types index ba1486785bdb0..f17f51958c809 100644 --- a/tests/baselines/reference/controlFlowTruthiness.types +++ b/tests/baselines/reference/controlFlowTruthiness.types @@ -181,7 +181,7 @@ function f8(x: T) { >x : T x; // {} ->x : T & {} +>x : NonNullable } else { x; // {} diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.errors.txt b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.errors.txt index 59e104ecd5ceb..9f4651b07c3fb 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.errors.txt +++ b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.errors.txt @@ -6,7 +6,6 @@ error TS2318: Cannot find global type 'Number'. error TS2318: Cannot find global type 'Object'. error TS2318: Cannot find global type 'RegExp'. error TS2318: Cannot find global type 'String'. -tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts(10,28): error TS2339: Property 'name' does not exist on type 'T["params"]'. !!! error TS2318: Cannot find global type 'Array'. @@ -17,7 +16,7 @@ tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts(10,28): error TS2339: Pr !!! error TS2318: Cannot find global type 'Object'. !!! error TS2318: Cannot find global type 'RegExp'. !!! error TS2318: Cannot find global type 'String'. -==== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts (1 errors) ==== +==== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts (0 errors) ==== type Readonly = {readonly [K in keyof T]: T[K]} interface A { params?: { name: string; }; @@ -28,8 +27,6 @@ tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts(10,28): error TS2339: Pr m() { this.attrs.params!.name; - ~~~~ -!!! error TS2339: Property 'name' does not exist on type 'T["params"]'. } } diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.symbols b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.symbols index 04e7779df1329..a41798f2f521b 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.symbols +++ b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.symbols @@ -29,11 +29,13 @@ class Test { >m : Symbol(Test.m, Decl(strictNullNotNullIndexTypeNoLib.ts, 6, 23)) this.attrs.params!.name; +>this.attrs.params!.name : Symbol(name, Decl(strictNullNotNullIndexTypeNoLib.ts, 2, 14)) >this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13)) >this.attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25)) >this : Symbol(Test, Decl(strictNullNotNullIndexTypeNoLib.ts, 3, 1)) >attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25)) >params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13)) +>name : Symbol(name, Decl(strictNullNotNullIndexTypeNoLib.ts, 2, 14)) } } diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types index 773b37039db03..aa2a723b09172 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types +++ b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types @@ -18,14 +18,14 @@ class Test { >m : () => void this.attrs.params!.name; ->this.attrs.params!.name : any ->this.attrs.params! : T["params"] +>this.attrs.params!.name : string +>this.attrs.params! : T["params"] & {} >this.attrs.params : T["params"] | undefined >this.attrs : Readonly >this : this >attrs : Readonly >params : T["params"] | undefined ->name : any +>name : string } } @@ -62,10 +62,10 @@ class Test2 { >attrs : Readonly m() { ->m : () => T["params"] +>m : () => T["params"] & {} return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally ->this.attrs.params! : T["params"] +>this.attrs.params! : T["params"] & {} >this.attrs.params : T["params"] | undefined >this.attrs : Readonly >this : this diff --git a/tests/baselines/reference/typeVariableTypeGuards.types b/tests/baselines/reference/typeVariableTypeGuards.types index a1c9e1c39431e..2274589cb8ce1 100644 --- a/tests/baselines/reference/typeVariableTypeGuards.types +++ b/tests/baselines/reference/typeVariableTypeGuards.types @@ -193,8 +193,8 @@ function f5(obj: T | undefined, key: K) { >obj : T | undefined obj[key]; ->obj[key] : (T & {})[K] ->obj : T & {} +>obj[key] : NonNullable[K] +>obj : NonNullable >key : K } } diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index b69c100b1e895..13633b8c7fb8d 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -100,7 +100,7 @@ function f11(x: T) { >x : T x; // T & {} ->x : T & {} +>x : NonNullable } else { x; // T @@ -115,7 +115,7 @@ function f11(x: T) { } else { x; // T & {} ->x : T & {} +>x : NonNullable } } @@ -255,7 +255,7 @@ function f21(x: T) { >undefined : undefined x; // T & {} ->x : T & {} +>x : NonNullable } else { x; // T @@ -267,7 +267,7 @@ function f21(x: T) { >null : null x; // T & {} ->x : T & {} +>x : NonNullable } else { x; // T @@ -325,7 +325,7 @@ function f22(x: T) { >undefined : undefined x; // T & {} ->x : T & {} +>x : NonNullable } else { x; // T @@ -337,7 +337,7 @@ function f22(x: T) { >null : null x; // T & {} ->x : T & {} +>x : NonNullable } else { x; // T @@ -372,7 +372,7 @@ function f23(x: T | undefined | null) { >undefined : undefined x; // T & {} ->x : T & {} +>x : NonNullable } if (x != null) { >x != null : boolean @@ -380,7 +380,7 @@ function f23(x: T | undefined | null) { >null : null x; // T & {} ->x : T & {} +>x : NonNullable } } @@ -417,7 +417,7 @@ function f31(x: T) { >x : T >typeof x === "object" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : T & {} +>x : NonNullable >"object" : "object" x; // T & object From d3eb84fe841cd2ffe35624f0fa3c68185d514d8f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 May 2022 08:57:07 -0700 Subject: [PATCH 24/30] Add fourslash test --- tests/cases/fourslash/completionPreferredSuggestions1.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/cases/fourslash/completionPreferredSuggestions1.ts diff --git a/tests/cases/fourslash/completionPreferredSuggestions1.ts b/tests/cases/fourslash/completionPreferredSuggestions1.ts new file mode 100644 index 0000000000000..cc426f4f265e0 --- /dev/null +++ b/tests/cases/fourslash/completionPreferredSuggestions1.ts @@ -0,0 +1,9 @@ +/// + +////declare let v1: string & {} | "a" | "b" | "c"; +////v1 = "/*1*/"; +////declare let v2: number & {} | 0 | 1 | 2; +////v2 = /*2*/; + +verify.completions({ marker: "1", includes: ["a", "b", "c"] }); +verify.completions({ marker: "2", includes: ["0", "1", "2"], isNewIdentifierLocation: true }); From 954e80a75dfa4c09c503a8bf5a27f79e67d8650c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 24 May 2022 15:26:37 -0700 Subject: [PATCH 25/30] More fourslash tests --- .../fourslash/completionPreferredSuggestions1.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/cases/fourslash/completionPreferredSuggestions1.ts b/tests/cases/fourslash/completionPreferredSuggestions1.ts index cc426f4f265e0..6b9430b4f4719 100644 --- a/tests/cases/fourslash/completionPreferredSuggestions1.ts +++ b/tests/cases/fourslash/completionPreferredSuggestions1.ts @@ -4,6 +4,17 @@ ////v1 = "/*1*/"; ////declare let v2: number & {} | 0 | 1 | 2; ////v2 = /*2*/; +////declare let v3: string & Record | "a" | "b" | "c"; +////v3 = "/*3*/"; +////type LiteralUnion1 = T | U & {}; +////type LiteralUnion2 = T | U & Record; +////declare let v4: LiteralUnion1<"a" | "b" | "c", string>; +////v4 = "/*4*/"; +////declare let v5: LiteralUnion2<"a" | "b" | "c", string>; +////v5 = "/*5*/"; verify.completions({ marker: "1", includes: ["a", "b", "c"] }); verify.completions({ marker: "2", includes: ["0", "1", "2"], isNewIdentifierLocation: true }); +verify.completions({ marker: "3", includes: ["a", "b", "c"] }); +verify.completions({ marker: "4", excludes: ["a", "b", "c"] }); +verify.completions({ marker: "5", includes: ["a", "b", "c"] }); From 1d9b53f7b3f45c0afffaf2823a2a32df57790644 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2022 11:43:38 -0700 Subject: [PATCH 26/30] Fix getFalsyFlags handling of intersections --- src/compiler/checker.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 48cf213041b5d..27e6f2b880807 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21146,9 +21146,11 @@ namespace ts { return getSupertypeOrUnion(types); } const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); - return primaryTypes.length ? - getNullableType(getSupertypeOrUnion(primaryTypes), getFalsyFlagsOfTypes(types) & TypeFlags.Nullable) : - getUnionType(types, UnionReduction.Subtype); + if (primaryTypes.length) { + const supertypeOrUnion = getSupertypeOrUnion(primaryTypes); + return primaryTypes === types ? supertypeOrUnion : getUnionType([supertypeOrUnion, ...filter(types, t => !!(t.flags & TypeFlags.Nullable))]); + } + return getUnionType(types, UnionReduction.Subtype); } // Return the leftmost type for which no type to the right is a subtype. @@ -21377,12 +21379,13 @@ namespace ts { // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns // no flags for all other types (including non-falsy literal types). function getFalsyFlags(type: Type): TypeFlags { - return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((type as UnionType).types) : - type.flags & TypeFlags.StringLiteral ? (type as StringLiteralType).value === "" ? TypeFlags.StringLiteral : 0 : - type.flags & TypeFlags.NumberLiteral ? (type as NumberLiteralType).value === 0 ? TypeFlags.NumberLiteral : 0 : - type.flags & TypeFlags.BigIntLiteral ? isZeroBigInt(type as BigIntLiteralType) ? TypeFlags.BigIntLiteral : 0 : - type.flags & TypeFlags.BooleanLiteral ? (type === falseType || type === regularFalseType) ? TypeFlags.BooleanLiteral : 0 : - type.flags & TypeFlags.PossiblyFalsy; + const t = type.flags & TypeFlags.Intersection ? getBaseConstraintOrType(type) : type; + return t.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((t as UnionType).types) : + t.flags & TypeFlags.StringLiteral ? (t as StringLiteralType).value === "" ? TypeFlags.StringLiteral : 0 : + t.flags & TypeFlags.NumberLiteral ? (t as NumberLiteralType).value === 0 ? TypeFlags.NumberLiteral : 0 : + t.flags & TypeFlags.BigIntLiteral ? isZeroBigInt(t as BigIntLiteralType) ? TypeFlags.BigIntLiteral : 0 : + t.flags & TypeFlags.BooleanLiteral ? (t === falseType || t === regularFalseType) ? TypeFlags.BooleanLiteral : 0 : + t.flags & TypeFlags.PossiblyFalsy; } function removeDefinitelyFalsyTypes(type: Type): Type { From 38159343da4d299af34d24620157323357e22cfa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2022 11:44:30 -0700 Subject: [PATCH 27/30] Accept new baselines --- .../reference/mappedTypes4.errors.txt | 77 ------------------- tests/baselines/reference/mappedTypes4.types | 10 +-- 2 files changed, 5 insertions(+), 82 deletions(-) delete mode 100644 tests/baselines/reference/mappedTypes4.errors.txt diff --git a/tests/baselines/reference/mappedTypes4.errors.txt b/tests/baselines/reference/mappedTypes4.errors.txt deleted file mode 100644 index e8b8032c24cd9..0000000000000 --- a/tests/baselines/reference/mappedTypes4.errors.txt +++ /dev/null @@ -1,77 +0,0 @@ -tests/cases/conformance/types/mapped/mappedTypes4.ts(11,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '(T & null) | (T & object)'. - - -==== tests/cases/conformance/types/mapped/mappedTypes4.ts (1 errors) ==== - type Box = { - }; - - type Boxified = { - [P in keyof T]: Box; - }; - - function boxify(obj: T): Boxified { - if (typeof obj === "object") { - let result = {} as Boxified; - for (let k in obj) { - ~~~ -!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '(T & null) | (T & object)'. - result[k] = { value: obj[k] }; - } - return result; - } - return obj; - } - - type A = { a: string }; - type B = { b: string }; - type C = { c: string }; - - function f1(x: A | B | C | undefined) { - return boxify(x); - } - - type T00 = Partial; - type T01 = Readonly; - type T02 = Boxified - type T03 = Readonly; - type T04 = Boxified; - type T05 = Partial<"hello" | "world" | 42>; - - type BoxifiedWithSentinel = { - [P in keyof T]: Box | U; - } - - type T10 = BoxifiedWithSentinel; - type T11 = BoxifiedWithSentinel; - type T12 = BoxifiedWithSentinel; - - type DeepReadonly = { - readonly [P in keyof T]: DeepReadonly; - }; - - type Foo = { - x: number; - y: { a: string, b: number }; - z: boolean; - }; - - type DeepReadonlyFoo = { - readonly x: number; - readonly y: { readonly a: string, readonly b: number }; - readonly z: boolean; - }; - - var x1: DeepReadonly; - var x1: DeepReadonlyFoo; - - // Repro from #13232 - - type Z = { a: number }; - type Clone = { - [P in keyof (T & {})]: T[P]; - }; - type M = Clone; // M should be { a: number } - - var z1: Z; - var z1: Clone; - \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypes4.types b/tests/baselines/reference/mappedTypes4.types index 5c09db18ec538..434fcf1f7e87f 100644 --- a/tests/baselines/reference/mappedTypes4.types +++ b/tests/baselines/reference/mappedTypes4.types @@ -30,14 +30,14 @@ function boxify(obj: T): Boxified { >obj : (T & null) | (T & object) result[k] = { value: obj[k] }; ->result[k] = { value: obj[k] } : { value: ((T & null) | (T & object))[Extract]; } +>result[k] = { value: obj[k] } : { value: NonNullable[Extract]; } >result[k] : Boxified[Extract] >result : Boxified >k : Extract ->{ value: obj[k] } : { value: ((T & null) | (T & object))[Extract]; } ->value : ((T & null) | (T & object))[Extract] ->obj[k] : ((T & null) | (T & object))[Extract] ->obj : (T & null) | (T & object) +>{ value: obj[k] } : { value: NonNullable[Extract]; } +>value : NonNullable[Extract] +>obj[k] : NonNullable[Extract] +>obj : NonNullable >k : Extract } return result; From bfa3a90104524262e60249561d7272e947df92ab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2022 12:57:38 -0700 Subject: [PATCH 28/30] Add constraint to compareProperties type parameter --- src/compiler/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a3be6f976cdc9..c6807e7260cb4 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2035,7 +2035,7 @@ namespace ts { return comparer(a, b); } - export function compareProperties(a: T | undefined, b: T | undefined, key: K, comparer: Comparer): Comparison { + export function compareProperties(a: T | undefined, b: T | undefined, key: K, comparer: Comparer): Comparison { return a === b ? Comparison.EqualTo : a === undefined ? Comparison.LessThan : b === undefined ? Comparison.GreaterThan : From 980166752b237ebd124d88c91c3a8d35808399a3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2022 15:09:00 -0700 Subject: [PATCH 29/30] Unconstrained type parameter not assignable to {} with strictNullChecks --- src/compiler/checker.ts | 10 +++------- src/compiler/diagnosticMessages.json | 4 ++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27e6f2b880807..9be0950e11d79 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18812,6 +18812,9 @@ namespace ts { return; } reportRelationError(headMessage, source, target); + if (strictNullChecks && source.flags & TypeFlags.TypeVariable && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable) && isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { + associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_probably_needs_an_extends_object_constraint)); + } } function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void { @@ -19616,13 +19619,6 @@ namespace ts { // IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch if (!(sourceFlags & TypeFlags.IndexedAccess && targetFlags & TypeFlags.IndexedAccess)) { const constraint = getConstraintOfType(source as TypeVariable) || unknownType; - if (!getConstraintOfType(source as TypeVariable) || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { - // A type variable with no constraint is not related to the non-primitive object type. - if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive), RecursionFlags.Both)) { - resetErrorInfo(saveErrorInfo); - return result; - } - } // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) { resetErrorInfo(saveErrorInfo); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index e7f5454e435c0..0bf7b8b3eb50e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1526,6 +1526,10 @@ "category": "Error", "code": 2207 }, + "This type parameter probably needs an `extends object` constraint.": { + "category": "Error", + "code": 2208 + }, "The project root is ambiguous, but is required to resolve export map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { "category": "Error", From 51da289ad7b37cc051237c17f194e6b371cb0dee Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2022 15:09:24 -0700 Subject: [PATCH 30/30] Accept new baselines --- .../reference/conditionalTypes1.errors.txt | 8 +- ...UnboundedTypeParamAssignability.errors.txt | 10 +- .../keyofAndIndexedAccess.errors.txt | 677 ++++++++++++++++++ ...mappedTypeAsClauseRelationships.errors.txt | 5 +- .../reference/mappedTypes6.errors.txt | 41 +- .../mappedTypesAndObjects.errors.txt | 54 ++ .../tsxNotUsingApparentTypeOfSFC.errors.txt | 22 +- .../reference/unknownType1.errors.txt | 6 +- 8 files changed, 817 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/keyofAndIndexedAccess.errors.txt create mode 100644 tests/baselines/reference/mappedTypesAndObjects.errors.txt diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt index 3c4c5340793b5..f411c6270e8f8 100644 --- a/tests/baselines/reference/conditionalTypes1.errors.txt +++ b/tests/baselines/reference/conditionalTypes1.errors.txt @@ -1,3 +1,5 @@ +tests/cases/conformance/types/conditional/conditionalTypes1.ts(12,5): error TS2322: Type 'T' is not assignable to type 'NonNullable'. + Type 'T' is not assignable to type '{}'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(17,5): error TS2322: Type 'T' is not assignable to type 'NonNullable'. Type 'string | undefined' is not assignable to type 'NonNullable'. Type 'undefined' is not assignable to type 'NonNullable'. @@ -66,7 +68,7 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS Type 'boolean' is not assignable to type 'true'. -==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (19 errors) ==== +==== tests/cases/conformance/types/conditional/conditionalTypes1.ts (20 errors) ==== type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" @@ -79,6 +81,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS function f1(x: T, y: NonNullable) { x = y; y = x; // Error + ~ +!!! error TS2322: Type 'T' is not assignable to type 'NonNullable'. +!!! error TS2322: Type 'T' is not assignable to type '{}'. +!!! related TS2208 tests/cases/conformance/types/conditional/conditionalTypes1.ts:10:13: This type parameter probably needs an `extends object` constraint. } function f2(x: T, y: NonNullable) { diff --git a/tests/baselines/reference/genericUnboundedTypeParamAssignability.errors.txt b/tests/baselines/reference/genericUnboundedTypeParamAssignability.errors.txt index b00dfc3c32857..6d7b87ebd3351 100644 --- a/tests/baselines/reference/genericUnboundedTypeParamAssignability.errors.txt +++ b/tests/baselines/reference/genericUnboundedTypeParamAssignability.errors.txt @@ -1,8 +1,10 @@ tests/cases/compiler/genericUnboundedTypeParamAssignability.ts(2,5): error TS2339: Property 'toString' does not exist on type 'T'. +tests/cases/compiler/genericUnboundedTypeParamAssignability.ts(15,6): error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'. +tests/cases/compiler/genericUnboundedTypeParamAssignability.ts(16,6): error TS2345: Argument of type 'T' is not assignable to parameter of type 'Record'. tests/cases/compiler/genericUnboundedTypeParamAssignability.ts(17,5): error TS2339: Property 'toString' does not exist on type 'T'. -==== tests/cases/compiler/genericUnboundedTypeParamAssignability.ts (2 errors) ==== +==== tests/cases/compiler/genericUnboundedTypeParamAssignability.ts (4 errors) ==== function f1(o: T) { o.toString(); // error ~~~~~~~~ @@ -20,7 +22,13 @@ tests/cases/compiler/genericUnboundedTypeParamAssignability.ts(17,5): error TS23 function user(t: T) { f1(t); f2(t); // error in strict, unbounded T doesn't satisfy the constraint + ~ +!!! error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'. +!!! related TS2208 tests/cases/compiler/genericUnboundedTypeParamAssignability.ts:13:15: This type parameter probably needs an `extends object` constraint. f3(t); // error in strict, unbounded T doesn't satisfy the constraint + ~ +!!! error TS2345: Argument of type 'T' is not assignable to parameter of type 'Record'. +!!! related TS2208 tests/cases/compiler/genericUnboundedTypeParamAssignability.ts:13:15: This type parameter probably needs an `extends object` constraint. t.toString(); // error, for the same reason as f1() ~~~~~~~~ !!! error TS2339: Property 'toString' does not exist on type 'T'. diff --git a/tests/baselines/reference/keyofAndIndexedAccess.errors.txt b/tests/baselines/reference/keyofAndIndexedAccess.errors.txt new file mode 100644 index 0000000000000..97bad1c12f5eb --- /dev/null +++ b/tests/baselines/reference/keyofAndIndexedAccess.errors.txt @@ -0,0 +1,677 @@ +tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(316,5): error TS2322: Type 'T' is not assignable to type '{}'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(317,5): error TS2322: Type 'T[keyof T]' is not assignable to type '{}'. + Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'. + Type 'T[string]' is not assignable to type '{}'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(318,5): error TS2322: Type 'T[K]' is not assignable to type '{}'. + Type 'T[keyof T]' is not assignable to type '{}'. + + +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts (3 errors) ==== + class Shape { + name: string; + width: number; + height: number; + visible: boolean; + } + + class TaggedShape extends Shape { + tag: string; + } + + class Item { + name: string; + price: number; + } + + class Options { + visible: "yes" | "no"; + } + + type Dictionary = { [x: string]: T }; + type NumericallyIndexed = { [x: number]: T }; + + const enum E { A, B, C } + + type K00 = keyof any; // string + type K01 = keyof string; // "toString" | "charAt" | ... + type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ... + type K03 = keyof boolean; // "valueOf" + type K04 = keyof void; // never + type K05 = keyof undefined; // never + type K06 = keyof null; // never + type K07 = keyof never; // string | number | symbol + type K08 = keyof unknown; // never + + type K10 = keyof Shape; // "name" | "width" | "height" | "visible" + type K11 = keyof Shape[]; // "length" | "toString" | ... + type K12 = keyof Dictionary; // string + type K13 = keyof {}; // never + type K14 = keyof Object; // "constructor" | "toString" | ... + type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ... + type K16 = keyof [string, number]; // "0" | "1" | "length" | "toString" | ... + type K17 = keyof (Shape | Item); // "name" + type K18 = keyof (Shape & Item); // "name" | "width" | "height" | "visible" | "price" + type K19 = keyof NumericallyIndexed // never + + type KeyOf = keyof T; + + type K20 = KeyOf; // "name" | "width" | "height" | "visible" + type K21 = KeyOf>; // string + + type NAME = "name"; + type WIDTH_OR_HEIGHT = "width" | "height"; + + type Q10 = Shape["name"]; // string + type Q11 = Shape["width" | "height"]; // number + type Q12 = Shape["name" | "visible"]; // string | boolean + + type Q20 = Shape[NAME]; // string + type Q21 = Shape[WIDTH_OR_HEIGHT]; // number + + type Q30 = [string, number][0]; // string + type Q31 = [string, number][1]; // number + type Q32 = [string, number][number]; // string | number + type Q33 = [string, number][E.A]; // string + type Q34 = [string, number][E.B]; // number + type Q35 = [string, number]["0"]; // string + type Q36 = [string, number]["1"]; // string + + type Q40 = (Shape | Options)["visible"]; // boolean | "yes" | "no" + type Q41 = (Shape & Options)["visible"]; // true & "yes" | true & "no" | false & "yes" | false & "no" + + type Q50 = Dictionary["howdy"]; // Shape + type Q51 = Dictionary[123]; // Shape + type Q52 = Dictionary[E.B]; // Shape + + declare let cond: boolean; + + function getProperty(obj: T, key: K) { + return obj[key]; + } + + function setProperty(obj: T, key: K, value: T[K]) { + obj[key] = value; + } + + function f10(shape: Shape) { + let name = getProperty(shape, "name"); // string + let widthOrHeight = getProperty(shape, cond ? "width" : "height"); // number + let nameOrVisible = getProperty(shape, cond ? "name" : "visible"); // string | boolean + setProperty(shape, "name", "rectangle"); + setProperty(shape, cond ? "width" : "height", 10); + setProperty(shape, cond ? "name" : "visible", true); // Technically not safe + } + + function f11(a: Shape[]) { + let len = getProperty(a, "length"); // number + setProperty(a, "length", len); + } + + function f12(t: [Shape, boolean]) { + let len = getProperty(t, "length"); + let s2 = getProperty(t, "0"); // Shape + let b2 = getProperty(t, "1"); // boolean + } + + function f13(foo: any, bar: any) { + let x = getProperty(foo, "x"); // any + let y = getProperty(foo, "100"); // any + let z = getProperty(foo, bar); // any + } + + class Component { + props: PropType; + getProperty(key: K) { + return this.props[key]; + } + setProperty(key: K, value: PropType[K]) { + this.props[key] = value; + } + } + + function f20(component: Component) { + let name = component.getProperty("name"); // string + let widthOrHeight = component.getProperty(cond ? "width" : "height"); // number + let nameOrVisible = component.getProperty(cond ? "name" : "visible"); // string | boolean + component.setProperty("name", "rectangle"); + component.setProperty(cond ? "width" : "height", 10) + component.setProperty(cond ? "name" : "visible", true); // Technically not safe + } + + function pluck(array: T[], key: K) { + return array.map(x => x[key]); + } + + function f30(shapes: Shape[]) { + let names = pluck(shapes, "name"); // string[] + let widths = pluck(shapes, "width"); // number[] + let nameOrVisibles = pluck(shapes, cond ? "name" : "visible"); // (string | boolean)[] + } + + function f31(key: K) { + const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; + return shape[key]; // Shape[K] + } + + function f32(key: K) { + const shape: Shape = { name: "foo", width: 5, height: 10, visible: true }; + return shape[key]; // Shape[K] + } + + function f33(shape: S, key: K) { + let name = getProperty(shape, "name"); + let prop = getProperty(shape, key); + return prop; + } + + function f34(ts: TaggedShape) { + let tag1 = f33(ts, "tag"); + let tag2 = getProperty(ts, "tag"); + } + + class C { + public x: string; + protected y: string; + private z: string; + } + + // Indexed access expressions have always permitted access to private and protected members. + // For consistency we also permit such access in indexed access types. + function f40(c: C) { + type X = C["x"]; + type Y = C["y"]; + type Z = C["z"]; + let x: X = c["x"]; + let y: Y = c["y"]; + let z: Z = c["z"]; + } + + function f50(k: keyof T, s: string) { + const x1 = s as keyof T; + const x2 = k as string; + } + + function f51(k: K, s: string) { + const x1 = s as keyof T; + const x2 = k as string; + } + + function f52(obj: { [x: string]: boolean }, k: Exclude, s: string, n: number) { + const x1 = obj[s]; + const x2 = obj[n]; + const x3 = obj[k]; + } + + function f53>(obj: { [x: string]: boolean }, k: K, s: string, n: number) { + const x1 = obj[s]; + const x2 = obj[n]; + const x3 = obj[k]; + } + + function f54(obj: T, key: keyof T) { + for (let s in obj[key]) { + } + const b = "foo" in obj[key]; + } + + function f55(obj: T, key: K) { + for (let s in obj[key]) { + } + const b = "foo" in obj[key]; + } + + function f60(source: T, target: T) { + for (let k in source) { + target[k] = source[k]; + } + } + + function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { + func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); + } + + function f71(func: (x: T, y: U) => Partial) { + let x = func({ a: 1, b: "hello" }, { c: true }); + x.a; // number | undefined + x.b; // string | undefined + x.c; // boolean | undefined + } + + function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean + } + + function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean + } + + function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { + let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean + } + + function f80(obj: T) { + let a1 = obj.a; // { x: any } + let a2 = obj['a']; // { x: any } + let a3 = obj['a'] as T['a']; // T["a"] + let x1 = obj.a.x; // any + let x2 = obj['a']['x']; // any + let x3 = obj['a']['x'] as T['a']['x']; // T["a"]["x"] + } + + function f81(obj: T) { + return obj['a']['x'] as T['a']['x']; + } + + function f82() { + let x1 = f81({ a: { x: "hello" } }); // string + let x2 = f81({ a: { x: 42 } }); // number + } + + function f83(obj: T, key: K) { + return obj[key]['x'] as T[K]['x']; + } + + function f84() { + let x1 = f83({ foo: { x: "hello" } }, "foo"); // string + let x2 = f83({ bar: { x: 42 } }, "bar"); // number + } + + class C1 { + x: number; + get(key: K) { + return this[key]; + } + set(key: K, value: this[K]) { + this[key] = value; + } + foo() { + let x1 = this.x; // number + let x2 = this["x"]; // number + let x3 = this.get("x"); // this["x"] + let x4 = getProperty(this, "x"); // this["x"] + this.x = 42; + this["x"] = 42; + this.set("x", 42); + setProperty(this, "x", 42); + } + } + + type S2 = { + a: string; + b: string; + }; + + function f90(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K]) { + x1 = x2; + x1 = x3; + x2 = x1; + x2 = x3; + x3 = x1; + x3 = x2; + x1.length; + x2.length; + x3.length; + } + + function f91(x: T, y: T[keyof T], z: T[K]) { + let a: {}; + a = x; + ~ +!!! error TS2322: Type 'T' is not assignable to type '{}'. +!!! related TS2208 tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts:314:14: This type parameter probably needs an `extends object` constraint. + a = y; + ~ +!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[string]' is not assignable to type '{}'. + a = z; + ~ +!!! error TS2322: Type 'T[K]' is not assignable to type '{}'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type '{}'. + } + + function f92(x: T, y: T[keyof T], z: T[K]) { + let a: {} | null | undefined; + a = x; + a = y; + a = z; + } + + // Repros from #12011 + + class Base { + get(prop: K) { + return this[prop]; + } + set(prop: K, value: this[K]) { + this[prop] = value; + } + } + + class Person extends Base { + parts: number; + constructor(parts: number) { + super(); + this.set("parts", parts); + } + getParts() { + return this.get("parts") + } + } + + class OtherPerson { + parts: number; + constructor(parts: number) { + setProperty(this, "parts", parts); + } + getParts() { + return getProperty(this, "parts") + } + } + + // Modified repro from #12544 + + function path(obj: T, key1: K1): T[K1]; + function path(obj: T, key1: K1, key2: K2): T[K1][K2]; + function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; + function path(obj: any, ...keys: (string | number)[]): any; + function path(obj: any, ...keys: (string | number)[]): any { + let result = obj; + for (let k of keys) { + result = result[k]; + } + return result; + } + + type Thing = { + a: { x: number, y: string }, + b: boolean + }; + + + function f1(thing: Thing) { + let x1 = path(thing, 'a'); // { x: number, y: string } + let x2 = path(thing, 'a', 'y'); // string + let x3 = path(thing, 'b'); // boolean + let x4 = path(thing, ...['a', 'x']); // any + } + + // Repro from comment in #12114 + + const assignTo2 = (object: T, key1: K1, key2: K2) => + (value: T[K1][K2]) => object[key1][key2] = value; + + // Modified repro from #12573 + + declare function one(handler: (t: T) => void): T + var empty = one(() => {}) // inferred as {}, expected + + type Handlers = { [K in keyof T]: (t: T[K]) => void } + declare function on(handlerHash: Handlers): T + var hashOfEmpty1 = on({ test: () => {} }); // {} + var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } + + // Repro from #12624 + + interface Options1 { + data?: Data + computed?: Computed; + } + + declare class Component1 { + constructor(options: Options1); + get(key: K): (Data & Computed)[K]; + } + + let c1 = new Component1({ + data: { + hello: "" + } + }); + + c1.get("hello"); + + // Repro from #12625 + + interface Options2 { + data?: Data + computed?: Computed; + } + + declare class Component2 { + constructor(options: Options2); + get(key: K): (Data & Computed)[K]; + } + + // Repro from #12641 + + interface R { + p: number; + } + + function f(p: K) { + let a: any; + a[p].add; // any + } + + // Repro from #12651 + + type MethodDescriptor = { + name: string; + args: any[]; + returnValue: any; + } + + declare function dispatchMethod(name: M['name'], args: M['args']): M['returnValue']; + + type SomeMethodDescriptor = { + name: "someMethod"; + args: [string, number]; + returnValue: string[]; + } + + let result = dispatchMethod("someMethod", ["hello", 35]); + + // Repro from #13073 + + type KeyTypes = "a" | "b" + let MyThingy: { [key in KeyTypes]: string[] }; + + function addToMyThingy(key: S) { + MyThingy[key].push("a"); + } + + // Repro from #13102 + + type Handler = { + onChange: (name: keyof T) => void; + }; + + function onChangeGenericFunction(handler: Handler) { + handler.onChange('preset') + } + + // Repro from #13285 + + function updateIds, K extends string>( + obj: T, + idFields: K[], + idMapping: Partial> + ): Record { + for (const idField of idFields) { + const newId: T[K] | undefined = idMapping[obj[idField]]; + if (newId) { + obj[idField] = newId; + } + } + return obj; + } + + // Repro from #13285 + + function updateIds2( + obj: T, + key: K, + stringMap: { [oldId: string]: string } + ) { + var x = obj[key]; + stringMap[x]; // Should be OK. + } + + // Repro from #13514 + + declare function head>(list: T): T[0]; + + // Repro from #13604 + + class A { + props: T & { foo: string }; + } + + class B extends A<{ x: number}> { + f(p: this["props"]) { + p.x; + } + } + + // Repro from #13749 + + class Form { + private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} + + public set(prop: K, value: T[K]) { + this.childFormFactories[prop](value) + } + } + + // Repro from #13787 + + class SampleClass