From eba78e42b8ab74cea160b2348e5323379958f90c Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 19 Jun 2023 17:30:58 -0700 Subject: [PATCH 1/2] use function that doesn't return unknown type --- src/compiler/checker.ts | 18 +++++---- ...discriminateWithMissingProperty.errors.txt | 22 ++++++++++ .../discriminateWithMissingProperty.symbols | 36 +++++++++++++++++ .../discriminateWithMissingProperty.types | 40 +++++++++++++++++++ .../discriminateWithMissingProperty.ts | 15 +++++++ 5 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/discriminateWithMissingProperty.errors.txt create mode 100644 tests/baselines/reference/discriminateWithMissingProperty.symbols create mode 100644 tests/baselines/reference/discriminateWithMissingProperty.types create mode 100644 tests/cases/compiler/discriminateWithMissingProperty.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 59be6353b3a86..b055a6b35a17d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10165,11 +10165,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return prop ? getTypeOfSymbol(prop) : undefined; } - function getTypeOfPropertyOrIndexSignature(type: Type, name: __String, addOptionalityToIndex: boolean): Type { - let propType; - return getTypeOfPropertyOfType(type, name) || - (propType = getApplicableIndexInfoForName(type, name)?.type) && addOptionality(propType, /*isProperty*/ true, addOptionalityToIndex) || - unknownType; + function getTypeOfPropertyOrIndexSignature(type: Type, name: __String): Type { + return getTypeOfPropertyOfType(type, name) || getApplicableIndexInfoForName(type, name)?.type || unknownType; } function isTypeAny(type: Type | undefined) { @@ -22703,7 +22700,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let matched = false; for (let i = 0; i < types.length; i++) { if (include[i]) { - const targetType = getTypeOfPropertyOrIndexSignature(types[i], propertyName, /*addOptionalityToIndex*/ true); + const targetType = getTypeOfPropertyOrIndexSignatureOfType(types[i], propertyName); if (targetType && related(getDiscriminatingType(), targetType)) { matched = true; } @@ -22721,6 +22718,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const filtered = contains(include, Ternary.False) ? getUnionType(types.filter((_, i) => include[i])) : target; return filtered.flags & TypeFlags.Never ? target : filtered; + + function getTypeOfPropertyOrIndexSignatureOfType(type: Type, name: __String): Type | undefined { + let propType; + return getTypeOfPropertyOfType(type, name) || + (propType = getApplicableIndexInfoForName(type, name)?.type) && + addOptionality(propType, /*isProperty*/ true, /*isOptional*/ true); + } } /** @@ -27016,7 +27020,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { propType = removeNullable && optionalChain ? getOptionalType(propType) : propType; const narrowedPropType = narrowType(propType); return filterType(type, t => { - const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName, /*addOptionalityToIndex*/ false); + const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName); return !(discriminantType.flags & TypeFlags.Never) && !(narrowedPropType.flags & TypeFlags.Never) && areTypesComparable(narrowedPropType, discriminantType); }); } diff --git a/tests/baselines/reference/discriminateWithMissingProperty.errors.txt b/tests/baselines/reference/discriminateWithMissingProperty.errors.txt new file mode 100644 index 0000000000000..f422ea2413d19 --- /dev/null +++ b/tests/baselines/reference/discriminateWithMissingProperty.errors.txt @@ -0,0 +1,22 @@ +discriminateWithMissingProperty.ts(12,5): error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array; }' is not assignable to parameter of type 'Arg'. + Types of property 'data' are incompatible. + Type 'Uint8Array' is not assignable to type 'number'. + + +==== discriminateWithMissingProperty.ts (1 errors) ==== + type Arg = { + mode: "numeric", + data: number, + } | { + mode: "alphabetic", + data: string, + } | { + data: string | Uint8Array; + } + + declare function foo(arg: Arg): void; + foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array; }' is not assignable to parameter of type 'Arg'. +!!! error TS2345: Types of property 'data' are incompatible. +!!! error TS2345: Type 'Uint8Array' is not assignable to type 'number'. \ No newline at end of file diff --git a/tests/baselines/reference/discriminateWithMissingProperty.symbols b/tests/baselines/reference/discriminateWithMissingProperty.symbols new file mode 100644 index 0000000000000..5f2a781896fc4 --- /dev/null +++ b/tests/baselines/reference/discriminateWithMissingProperty.symbols @@ -0,0 +1,36 @@ +//// [tests/cases/compiler/discriminateWithMissingProperty.ts] //// + +=== discriminateWithMissingProperty.ts === +type Arg = { +>Arg : Symbol(Arg, Decl(discriminateWithMissingProperty.ts, 0, 0)) + + mode: "numeric", +>mode : Symbol(mode, Decl(discriminateWithMissingProperty.ts, 0, 12)) + + data: number, +>data : Symbol(data, Decl(discriminateWithMissingProperty.ts, 1, 20)) + +} | { + mode: "alphabetic", +>mode : Symbol(mode, Decl(discriminateWithMissingProperty.ts, 3, 5)) + + data: string, +>data : Symbol(data, Decl(discriminateWithMissingProperty.ts, 4, 23)) + +} | { + data: string | Uint8Array; +>data : Symbol(data, Decl(discriminateWithMissingProperty.ts, 6, 5)) +>Uint8Array : Symbol(Uint8Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +declare function foo(arg: Arg): void; +>foo : Symbol(foo, Decl(discriminateWithMissingProperty.ts, 8, 1)) +>arg : Symbol(arg, Decl(discriminateWithMissingProperty.ts, 10, 21)) +>Arg : Symbol(Arg, Decl(discriminateWithMissingProperty.ts, 0, 0)) + +foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error +>foo : Symbol(foo, Decl(discriminateWithMissingProperty.ts, 8, 1)) +>mode : Symbol(mode, Decl(discriminateWithMissingProperty.ts, 11, 5)) +>data : Symbol(data, Decl(discriminateWithMissingProperty.ts, 11, 22)) +>Uint8Array : Symbol(Uint8Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/discriminateWithMissingProperty.types b/tests/baselines/reference/discriminateWithMissingProperty.types new file mode 100644 index 0000000000000..982a4da2de393 --- /dev/null +++ b/tests/baselines/reference/discriminateWithMissingProperty.types @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/discriminateWithMissingProperty.ts] //// + +=== discriminateWithMissingProperty.ts === +type Arg = { +>Arg : { mode: "numeric"; data: number; } | { mode: "alphabetic"; data: string; } | { data: string | Uint8Array; } + + mode: "numeric", +>mode : "numeric" + + data: number, +>data : number + +} | { + mode: "alphabetic", +>mode : "alphabetic" + + data: string, +>data : string + +} | { + data: string | Uint8Array; +>data : string | Uint8Array +} + +declare function foo(arg: Arg): void; +>foo : (arg: Arg) => void +>arg : Arg + +foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error +>foo({ mode: "numeric", data: new Uint8Array([30]) }) : void +>foo : (arg: Arg) => void +>{ mode: "numeric", data: new Uint8Array([30]) } : { mode: "numeric"; data: Uint8Array; } +>mode : "numeric" +>"numeric" : "numeric" +>data : Uint8Array +>new Uint8Array([30]) : Uint8Array +>Uint8Array : Uint8ArrayConstructor +>[30] : number[] +>30 : 30 + diff --git a/tests/cases/compiler/discriminateWithMissingProperty.ts b/tests/cases/compiler/discriminateWithMissingProperty.ts new file mode 100644 index 0000000000000..d27bc03f54a70 --- /dev/null +++ b/tests/cases/compiler/discriminateWithMissingProperty.ts @@ -0,0 +1,15 @@ +// @strict: true +// @noEmit: true + +type Arg = { + mode: "numeric", + data: number, +} | { + mode: "alphabetic", + data: string, +} | { + data: string | Uint8Array; +} + +declare function foo(arg: Arg): void; +foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error \ No newline at end of file From 8cf0b6d3491e802b1944a4652b98502a5ceb1e67 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 20 Jun 2023 11:16:21 -0700 Subject: [PATCH 2/2] refactor: move function --- src/compiler/checker.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b055a6b35a17d..b2fca5ca4d718 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10169,6 +10169,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfPropertyOfType(type, name) || getApplicableIndexInfoForName(type, name)?.type || unknownType; } + /** + * Similar to `getTypeOfPropertyOrIndexSignature`, + * but returns `undefined` if there is no matching property or index signature, + * and adds optionality to index signature types. + */ + function getTypeOfPropertyOrIndexSignatureOfType(type: Type, name: __String): Type | undefined { + let propType; + return getTypeOfPropertyOfType(type, name) || + (propType = getApplicableIndexInfoForName(type, name)?.type) && + addOptionality(propType, /*isProperty*/ true, /*isOptional*/ true); + } + function isTypeAny(type: Type | undefined) { return type && (type.flags & TypeFlags.Any) !== 0; } @@ -22718,13 +22730,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const filtered = contains(include, Ternary.False) ? getUnionType(types.filter((_, i) => include[i])) : target; return filtered.flags & TypeFlags.Never ? target : filtered; - - function getTypeOfPropertyOrIndexSignatureOfType(type: Type, name: __String): Type | undefined { - let propType; - return getTypeOfPropertyOfType(type, name) || - (propType = getApplicableIndexInfoForName(type, name)?.type) && - addOptionality(propType, /*isProperty*/ true, /*isOptional*/ true); - } } /**