Allow discrimination to identical object types when discriminating contextual types (#40574)

* Merge identical object types when discriminating contextual types

Co-authored-by: Orta <ortam@microsoft.com>

* Allow identical discriminants when discriminating, rather than trying to unify identical union members

* Fix lint

Co-authored-by: Orta <ortam@microsoft.com>
This commit is contained in:
Wesley Wigham 2020-09-23 00:51:14 -07:00 committed by GitHub
parent ad2a07440c
commit a91c2879ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 159 additions and 2 deletions

View File

@ -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];
}
/**

View File

@ -4170,7 +4170,7 @@ namespace ts {
export const enum UnionReduction {
None = 0,
Literal,
Subtype
Subtype,
}
/* @internal */

View File

@ -0,0 +1,25 @@
//// [inferenceUnionOfObjectsMappedContextualType.ts]
type Entity = {
someDate: Date | null;
} & ({ id: string; } | { id: number; })
type RowRendererMeta<TInput extends {}> = {
[key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; };
}
type RowRenderer<TInput extends {}> = RowRendererMeta<TInput>[keyof RowRendererMeta<TInput>];
const test: RowRenderer<Entity> = {
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
};

View File

@ -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<TInput extends {}> = {
>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<TInput extends {}> = RowRendererMeta<TInput>[keyof RowRendererMeta<TInput>];
>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<Entity> = {
>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, --, --))
}

View File

@ -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<TInput extends {}> = {
>RowRendererMeta : RowRendererMeta<TInput>
[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<TInput extends {}> = RowRendererMeta<TInput>[keyof RowRendererMeta<TInput>];
>RowRenderer : RowRenderer<TInput>
const test: RowRenderer<Entity> = {
>test : RowRenderer<Entity>
>{ 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
>'-' : "-"
}

View File

@ -0,0 +1,16 @@
// @strict: true
type Entity = {
someDate: Date | null;
} & ({ id: string; } | { id: number; })
type RowRendererMeta<TInput extends {}> = {
[key in keyof TInput]: { key: key; caption: string; formatter?: (value: TInput[key]) => string; };
}
type RowRenderer<TInput extends {}> = RowRendererMeta<TInput>[keyof RowRendererMeta<TInput>];
const test: RowRenderer<Entity> = {
key: 'someDate',
caption: 'My Date',
formatter: (value) => value ? value.toString() : '-' // value: any
}