diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 59be6353b3a86..b2fca5ca4d718 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10165,11 +10165,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return prop ? getTypeOfSymbol(prop) : undefined; } - function getTypeOfPropertyOrIndexSignature(type: Type, name: __String, addOptionalityToIndex: boolean): Type { + function getTypeOfPropertyOrIndexSignature(type: Type, name: __String): Type { + 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, addOptionalityToIndex) || - unknownType; + (propType = getApplicableIndexInfoForName(type, name)?.type) && + addOptionality(propType, /*isProperty*/ true, /*isOptional*/ true); } function isTypeAny(type: Type | undefined) { @@ -22703,7 +22712,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; } @@ -27016,7 +27025,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