diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a131b3c55c2..d6751e97654 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18348,8 +18348,18 @@ namespace ts { } } const match = discriminable.indexOf(/*searchElement*/ true); + if (match === -1) { + return defaultValue; + } // make sure exactly 1 matches before returning it - return match === -1 || discriminable.indexOf(/*searchElement*/ true, match + 1) !== -1 ? defaultValue : target.types[match]; + let nextMatch = discriminable.indexOf(/*searchElement*/ true, match + 1); + while (nextMatch !== -1) { + if (!isTypeIdenticalTo(target.types[match], target.types[nextMatch])) { + return defaultValue; + } + nextMatch = discriminable.indexOf(/*searchElement*/ true, nextMatch + 1); + } + return target.types[match]; } /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fbf308707a1..6448ded8759 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4170,7 +4170,7 @@ namespace ts { export const enum UnionReduction { None = 0, Literal, - Subtype + Subtype, } /* @internal */ diff --git a/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.js b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.js new file mode 100644 index 00000000000..b02b9ddf25d --- /dev/null +++ b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.js @@ -0,0 +1,25 @@ +//// [inferenceUnionOfObjectsMappedContextualType.ts] +type Entity = { + someDate: Date | null; +} & ({ id: string; } | { id: number; }) + +type RowRendererMeta = { + [key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; }; +} + +type RowRenderer = RowRendererMeta[keyof RowRendererMeta]; + +const test: RowRenderer = { + key: 'someDate', + caption: 'My Date', + formatter: (value) => value ? value.toString() : '-' // value: any +} + + +//// [inferenceUnionOfObjectsMappedContextualType.js] +"use strict"; +var test = { + key: 'someDate', + caption: 'My Date', + formatter: function (value) { return value ? value.toString() : '-'; } // value: any +}; diff --git a/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.symbols b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.symbols new file mode 100644 index 00000000000..08d473b61cd --- /dev/null +++ b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/inferenceUnionOfObjectsMappedContextualType.ts === +type Entity = { +>Entity : Symbol(Entity, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 0, 0)) + + someDate: Date | null; +>someDate : Symbol(someDate, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 0, 15)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +} & ({ id: string; } | { id: number; }) +>id : Symbol(id, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 2, 6)) +>id : Symbol(id, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 2, 24)) + +type RowRendererMeta = { +>RowRendererMeta : Symbol(RowRendererMeta, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 2, 39)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 4, 21)) + + [key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; }; +>key : Symbol(key, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 5)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 4, 21)) +>key : Symbol(key, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 28)) +>key : Symbol(key, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 5)) +>caption : Symbol(caption, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 38)) +>formatter : Symbol(formatter, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 55)) +>value : Symbol(value, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 69)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 4, 21)) +>key : Symbol(key, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 5, 5)) +} + +type RowRenderer = RowRendererMeta[keyof RowRendererMeta]; +>RowRenderer : Symbol(RowRenderer, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 6, 1)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 8, 17)) +>RowRendererMeta : Symbol(RowRendererMeta, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 2, 39)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 8, 17)) +>RowRendererMeta : Symbol(RowRendererMeta, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 2, 39)) +>TInput : Symbol(TInput, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 8, 17)) + +const test: RowRenderer = { +>test : Symbol(test, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 10, 5)) +>RowRenderer : Symbol(RowRenderer, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 6, 1)) +>Entity : Symbol(Entity, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 0, 0)) + + key: 'someDate', +>key : Symbol(key, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 10, 35)) + + caption: 'My Date', +>caption : Symbol(caption, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 11, 20)) + + formatter: (value) => value ? value.toString() : '-' // value: any +>formatter : Symbol(formatter, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 12, 23)) +>value : Symbol(value, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 13, 16)) +>value : Symbol(value, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 13, 16)) +>value.toString : Symbol(Date.toString, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(inferenceUnionOfObjectsMappedContextualType.ts, 13, 16)) +>toString : Symbol(Date.toString, Decl(lib.es5.d.ts, --, --)) +} + diff --git a/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.types b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.types new file mode 100644 index 00000000000..7a0dc327a49 --- /dev/null +++ b/tests/baselines/reference/inferenceUnionOfObjectsMappedContextualType.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/inferenceUnionOfObjectsMappedContextualType.ts === +type Entity = { +>Entity : Entity + + someDate: Date | null; +>someDate : Date | null +>null : null + +} & ({ id: string; } | { id: number; }) +>id : string +>id : number + +type RowRendererMeta = { +>RowRendererMeta : RowRendererMeta + + [key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; }; +>key : key +>caption : string +>formatter : ((value: TInput[key]) => string) | undefined +>value : TInput[key] +} + +type RowRenderer = RowRendererMeta[keyof RowRendererMeta]; +>RowRenderer : RowRenderer + +const test: RowRenderer = { +>test : RowRenderer +>{ key: 'someDate', caption: 'My Date', formatter: (value) => value ? value.toString() : '-' // value: any} : { key: "someDate"; caption: string; formatter: (value: Date | null) => string; } + + key: 'someDate', +>key : "someDate" +>'someDate' : "someDate" + + caption: 'My Date', +>caption : string +>'My Date' : "My Date" + + formatter: (value) => value ? value.toString() : '-' // value: any +>formatter : (value: Date | null) => string +>(value) => value ? value.toString() : '-' : (value: Date | null) => string +>value : Date | null +>value ? value.toString() : '-' : string +>value : Date | null +>value.toString() : string +>value.toString : () => string +>value : Date +>toString : () => string +>'-' : "-" +} + diff --git a/tests/cases/compiler/inferenceUnionOfObjectsMappedContextualType.ts b/tests/cases/compiler/inferenceUnionOfObjectsMappedContextualType.ts new file mode 100644 index 00000000000..2a91b6fb5f3 --- /dev/null +++ b/tests/cases/compiler/inferenceUnionOfObjectsMappedContextualType.ts @@ -0,0 +1,16 @@ +// @strict: true +type Entity = { + someDate: Date | null; +} & ({ id: string; } | { id: number; }) + +type RowRendererMeta = { + [key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; }; +} + +type RowRenderer = RowRendererMeta[keyof RowRendererMeta]; + +const test: RowRenderer = { + key: 'someDate', + caption: 'My Date', + formatter: (value) => value ? value.toString() : '-' // value: any +}