From 7a1c5b7a20886d7e08e03fa333e078f29f400ca5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 12 Feb 2020 15:05:01 -0800 Subject: [PATCH] Avoid expensive relationship checking in mapped type member resolution (#36754) * Avoid expensive relationship checking in mapped type member resolution * Accept new baselines --- src/compiler/checker.ts | 4 ++-- .../definiteAssignmentOfDestructuredVariable.types | 12 ++++++------ .../reference/strictNullNotNullIndexTypeNoLib.types | 8 ++++---- .../strictNullNotNullIndexTypeShouldWork.types | 8 ++++---- .../baselines/reference/typeVariableTypeGuards.types | 6 +++--- .../reference/voidReturnIndexUnionInference.types | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c4535f4fcf6e..83cb9045a03da 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9590,7 +9590,7 @@ namespace ts { // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks // mode, if the underlying property is optional we remove 'undefined' from the type. - prop.type = strictNullChecks && isOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) : + prop.type = strictNullChecks && isOptional && !maybeTypeOfKind(propType, TypeFlags.Undefined | TypeFlags.Void) ? getOptionalType(propType) : strictNullChecks && !isOptional && modifiersProp && modifiersProp.flags & SymbolFlags.Optional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : propType; if (modifiersProp) { @@ -13695,7 +13695,7 @@ namespace ts { const templateMapper = combineTypeMappers(mapper, createTypeMapper([getTypeParameterFromMappedType(type)], [key])); const propType = instantiateType(getTemplateTypeFromMappedType(type.target || type), templateMapper); const modifiers = getMappedTypeModifiers(type); - return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) : + return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !maybeTypeOfKind(propType, TypeFlags.Undefined | TypeFlags.Void) ? getOptionalType(propType) : strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : propType; } diff --git a/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types index 96d632736dd84..6ae08a4ae0cd2 100644 --- a/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types +++ b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types @@ -18,20 +18,20 @@ class C { >method : () => void let { a, b } = this.foo; ->a : T["a"] +>a : T["a"] | undefined >b : T["b"] >this.foo : { [P in keyof T]: T[P]; } >this : this >foo : { [P in keyof T]: T[P]; } !(a && b); ->!(a && b) : false ->(a && b) : T["b"] ->a && b : T["b"] ->a : T["a"] +>!(a && b) : boolean +>(a && b) : T["b"] | undefined +>a && b : T["b"] | undefined +>a : T["a"] | undefined >b : T["b"] a; ->a : T["a"] +>a : T["a"] | undefined } } diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types index 295ffad96bdac..773b37039db03 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types +++ b/tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types @@ -20,11 +20,11 @@ class Test { this.attrs.params!.name; >this.attrs.params!.name : any >this.attrs.params! : T["params"] ->this.attrs.params : T["params"] +>this.attrs.params : T["params"] | undefined >this.attrs : Readonly >this : this >attrs : Readonly ->params : T["params"] +>params : T["params"] | undefined >name : any } } @@ -66,10 +66,10 @@ class Test2 { 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 >attrs : Readonly ->params : T["params"] +>params : T["params"] | undefined } } diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types b/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types index 2bfd1c7c5f3f4..102b345109c26 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types +++ b/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types @@ -17,11 +17,11 @@ class Test { this.attrs.params!.name; >this.attrs.params!.name : string >this.attrs.params! : NonNullable ->this.attrs.params : T["params"] +>this.attrs.params : T["params"] | undefined >this.attrs : Readonly >this : this >attrs : Readonly ->params : T["params"] +>params : T["params"] | undefined >name : string } } @@ -63,10 +63,10 @@ class Test2 { return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally >this.attrs.params! : NonNullable ->this.attrs.params : T["params"] +>this.attrs.params : T["params"] | undefined >this.attrs : Readonly >this : this >attrs : Readonly ->params : T["params"] +>params : T["params"] | undefined } } diff --git a/tests/baselines/reference/typeVariableTypeGuards.types b/tests/baselines/reference/typeVariableTypeGuards.types index 0bc2f4a838971..063f6bbc96d55 100644 --- a/tests/baselines/reference/typeVariableTypeGuards.types +++ b/tests/baselines/reference/typeVariableTypeGuards.types @@ -16,12 +16,12 @@ class A

> { >doSomething : () => void this.props.foo && this.props.foo() ->this.props.foo && this.props.foo() : void ->this.props.foo : P["foo"] +>this.props.foo && this.props.foo() : void | undefined +>this.props.foo : P["foo"] | undefined >this.props : Readonly

>this : this >props : Readonly

->foo : P["foo"] +>foo : P["foo"] | undefined >this.props.foo() : void >this.props.foo : () => void >this.props : Readonly

diff --git a/tests/baselines/reference/voidReturnIndexUnionInference.types b/tests/baselines/reference/voidReturnIndexUnionInference.types index 20a962b9fef16..fa7d87b8f63af 100644 --- a/tests/baselines/reference/voidReturnIndexUnionInference.types +++ b/tests/baselines/reference/voidReturnIndexUnionInference.types @@ -43,9 +43,9 @@ function bad

(props: Readonly

) { safeInvoke(props.onFoo, "blah"); >safeInvoke(props.onFoo, "blah") : boolean | undefined >safeInvoke : (func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined ->props.onFoo : P["onFoo"] +>props.onFoo : P["onFoo"] | undefined >props : Readonly

->onFoo : P["onFoo"] +>onFoo : P["onFoo"] | undefined >"blah" : "blah" // ERROR HERE!!! @@ -53,9 +53,9 @@ function bad

(props: Readonly

) { safeInvoke(props.onBar, "blah"); >safeInvoke(props.onBar, "blah") : void | undefined >safeInvoke : (func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined ->props.onBar : P["onBar"] +>props.onBar : P["onBar"] | undefined >props : Readonly

->onBar : P["onBar"] +>onBar : P["onBar"] | undefined >"blah" : "blah" }