mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 02:30:15 -06:00
Merge pull request #5737 from Microsoft/unionIntersectionTypeInference
Improved union/intersection type inference Conflicts: src/compiler/checker.ts
This commit is contained in:
parent
a1ff917aa2
commit
b022893fd8
@ -43,6 +43,8 @@ namespace ts {
|
||||
let emptyArray: any[] = [];
|
||||
let emptySymbols: SymbolTable = {};
|
||||
|
||||
let jsxElementClassType: Type = undefined;
|
||||
|
||||
let compilerOptions = host.getCompilerOptions();
|
||||
let languageVersion = compilerOptions.target || ScriptTarget.ES3;
|
||||
let modulekind = compilerOptions.module ? compilerOptions.module : languageVersion === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.None;
|
||||
@ -5928,6 +5930,17 @@ namespace ts {
|
||||
}
|
||||
|
||||
function inferFromTypes(source: Type, target: Type) {
|
||||
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
|
||||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
|
||||
// Source and target are both unions or both intersections. To improve the quality of
|
||||
// inferences we first reduce the types by removing constituents that are identically
|
||||
// matched by a constituent in the other type. For example, when inferring from
|
||||
// 'string | string[]' to 'string | T', we reduce the types to 'string[]' and 'T'.
|
||||
const reducedSource = reduceUnionOrIntersectionType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
|
||||
const reducedTarget = reduceUnionOrIntersectionType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source);
|
||||
source = reducedSource;
|
||||
target = reducedTarget;
|
||||
}
|
||||
if (target.flags & TypeFlags.TypeParameter) {
|
||||
// If target is a type parameter, make an inference, unless the source type contains
|
||||
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
|
||||
@ -5938,8 +5951,7 @@ namespace ts {
|
||||
if (source.flags & TypeFlags.ContainsAnyFunctionType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let typeParameters = context.typeParameters;
|
||||
const typeParameters = context.typeParameters;
|
||||
for (let i = 0; i < typeParameters.length; i++) {
|
||||
if (target === typeParameters[i]) {
|
||||
let inferences = context.inferences[i];
|
||||
@ -6086,6 +6098,41 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function typeIdenticalToSomeType(source: Type, target: UnionOrIntersectionType): boolean {
|
||||
for (const t of target.types) {
|
||||
if (isTypeIdenticalTo(source, t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the reduced form of the source type. This type is computed by by removing all source
|
||||
* constituents that have an identical match in the target type.
|
||||
*/
|
||||
function reduceUnionOrIntersectionType(source: UnionOrIntersectionType, target: UnionOrIntersectionType) {
|
||||
let sourceTypes = source.types;
|
||||
let sourceIndex = 0;
|
||||
let modified = false;
|
||||
while (sourceIndex < sourceTypes.length) {
|
||||
if (typeIdenticalToSomeType(sourceTypes[sourceIndex], target)) {
|
||||
if (!modified) {
|
||||
sourceTypes = sourceTypes.slice(0);
|
||||
modified = true;
|
||||
}
|
||||
sourceTypes.splice(sourceIndex, 1);
|
||||
}
|
||||
else {
|
||||
sourceIndex++;
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
|
||||
let inferences = context.inferences[index];
|
||||
return inferences.primary || inferences.secondary || emptyArray;
|
||||
@ -7859,7 +7906,6 @@ namespace ts {
|
||||
return prop || unknownSymbol;
|
||||
}
|
||||
|
||||
let jsxElementClassType: Type = undefined;
|
||||
function getJsxGlobalElementClassType(): Type {
|
||||
if (!jsxElementClassType) {
|
||||
jsxElementClassType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.ElementClass);
|
||||
|
||||
113
tests/baselines/reference/unionAndIntersectionInference1.js
Normal file
113
tests/baselines/reference/unionAndIntersectionInference1.js
Normal file
@ -0,0 +1,113 @@
|
||||
//// [unionAndIntersectionInference1.ts]
|
||||
// Repro from #2264
|
||||
|
||||
interface Y { 'i am a very certain type': Y }
|
||||
var y: Y = <Y>undefined;
|
||||
function destructure<a, r>(
|
||||
something: a | Y,
|
||||
haveValue: (value: a) => r,
|
||||
haveY: (value: Y) => r
|
||||
): r {
|
||||
return something === y ? haveY(y) : haveValue(<a>something);
|
||||
}
|
||||
|
||||
var value = Math.random() > 0.5 ? 'hey!' : <Y>undefined;
|
||||
|
||||
var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y
|
||||
|
||||
// Repro from #4212
|
||||
|
||||
function isVoid<a>(value: void | a): value is void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isNonVoid<a>(value: void | a) : value is a {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function foo1<a>(value: void|a): void {
|
||||
if (isVoid(value)) {
|
||||
value; // value is void
|
||||
} else {
|
||||
value; // value is a
|
||||
}
|
||||
}
|
||||
|
||||
function baz1<a>(value: void|a): void {
|
||||
if (isNonVoid(value)) {
|
||||
value; // value is a
|
||||
} else {
|
||||
value; // value is void
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #5417
|
||||
|
||||
type Maybe<T> = T | void;
|
||||
|
||||
function get<U>(x: U | void): U {
|
||||
return null; // just an example
|
||||
}
|
||||
|
||||
let foo: Maybe<string>;
|
||||
get(foo).toUpperCase(); // Ok
|
||||
|
||||
// Repro from #5456
|
||||
|
||||
interface Man {
|
||||
walks: boolean;
|
||||
}
|
||||
|
||||
interface Bear {
|
||||
roars: boolean;
|
||||
}
|
||||
|
||||
interface Pig {
|
||||
oinks: boolean;
|
||||
}
|
||||
|
||||
declare function pigify<T>(y: T & Bear): T & Pig;
|
||||
declare var mbp: Man & Bear;
|
||||
|
||||
pigify(mbp).oinks; // OK, mbp is treated as Pig
|
||||
pigify(mbp).walks; // Ok, mbp is treated as Man
|
||||
|
||||
|
||||
//// [unionAndIntersectionInference1.js]
|
||||
// Repro from #2264
|
||||
var y = undefined;
|
||||
function destructure(something, haveValue, haveY) {
|
||||
return something === y ? haveY(y) : haveValue(something);
|
||||
}
|
||||
var value = Math.random() > 0.5 ? 'hey!' : undefined;
|
||||
var result = destructure(value, function (text) { return 'string'; }, function (y) { return 'other one'; }); // text: string, y: Y
|
||||
// Repro from #4212
|
||||
function isVoid(value) {
|
||||
return undefined;
|
||||
}
|
||||
function isNonVoid(value) {
|
||||
return undefined;
|
||||
}
|
||||
function foo1(value) {
|
||||
if (isVoid(value)) {
|
||||
value; // value is void
|
||||
}
|
||||
else {
|
||||
value; // value is a
|
||||
}
|
||||
}
|
||||
function baz1(value) {
|
||||
if (isNonVoid(value)) {
|
||||
value; // value is a
|
||||
}
|
||||
else {
|
||||
value; // value is void
|
||||
}
|
||||
}
|
||||
function get(x) {
|
||||
return null; // just an example
|
||||
}
|
||||
var foo;
|
||||
get(foo).toUpperCase(); // Ok
|
||||
pigify(mbp).oinks; // OK, mbp is treated as Pig
|
||||
pigify(mbp).walks; // Ok, mbp is treated as Man
|
||||
202
tests/baselines/reference/unionAndIntersectionInference1.symbols
Normal file
202
tests/baselines/reference/unionAndIntersectionInference1.symbols
Normal file
@ -0,0 +1,202 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts ===
|
||||
// Repro from #2264
|
||||
|
||||
interface Y { 'i am a very certain type': Y }
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
|
||||
var y: Y = <Y>undefined;
|
||||
>y : Symbol(y, Decl(unionAndIntersectionInference1.ts, 3, 3))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
function destructure<a, r>(
|
||||
>destructure : Symbol(destructure, Decl(unionAndIntersectionInference1.ts, 3, 24))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 4, 21))
|
||||
>r : Symbol(r, Decl(unionAndIntersectionInference1.ts, 4, 23))
|
||||
|
||||
something: a | Y,
|
||||
>something : Symbol(something, Decl(unionAndIntersectionInference1.ts, 4, 27))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 4, 21))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
|
||||
haveValue: (value: a) => r,
|
||||
>haveValue : Symbol(haveValue, Decl(unionAndIntersectionInference1.ts, 5, 21))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 6, 16))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 4, 21))
|
||||
>r : Symbol(r, Decl(unionAndIntersectionInference1.ts, 4, 23))
|
||||
|
||||
haveY: (value: Y) => r
|
||||
>haveY : Symbol(haveY, Decl(unionAndIntersectionInference1.ts, 6, 31))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 7, 12))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
>r : Symbol(r, Decl(unionAndIntersectionInference1.ts, 4, 23))
|
||||
|
||||
): r {
|
||||
>r : Symbol(r, Decl(unionAndIntersectionInference1.ts, 4, 23))
|
||||
|
||||
return something === y ? haveY(y) : haveValue(<a>something);
|
||||
>something : Symbol(something, Decl(unionAndIntersectionInference1.ts, 4, 27))
|
||||
>y : Symbol(y, Decl(unionAndIntersectionInference1.ts, 3, 3))
|
||||
>haveY : Symbol(haveY, Decl(unionAndIntersectionInference1.ts, 6, 31))
|
||||
>y : Symbol(y, Decl(unionAndIntersectionInference1.ts, 3, 3))
|
||||
>haveValue : Symbol(haveValue, Decl(unionAndIntersectionInference1.ts, 5, 21))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 4, 21))
|
||||
>something : Symbol(something, Decl(unionAndIntersectionInference1.ts, 4, 27))
|
||||
}
|
||||
|
||||
var value = Math.random() > 0.5 ? 'hey!' : <Y>undefined;
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 12, 3))
|
||||
>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --))
|
||||
>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
>random : Symbol(Math.random, Decl(lib.d.ts, --, --))
|
||||
>Y : Symbol(Y, Decl(unionAndIntersectionInference1.ts, 0, 0))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y
|
||||
>result : Symbol(result, Decl(unionAndIntersectionInference1.ts, 14, 3))
|
||||
>destructure : Symbol(destructure, Decl(unionAndIntersectionInference1.ts, 3, 24))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 12, 3))
|
||||
>text : Symbol(text, Decl(unionAndIntersectionInference1.ts, 14, 31))
|
||||
>y : Symbol(y, Decl(unionAndIntersectionInference1.ts, 14, 49))
|
||||
|
||||
// Repro from #4212
|
||||
|
||||
function isVoid<a>(value: void | a): value is void {
|
||||
>isVoid : Symbol(isVoid, Decl(unionAndIntersectionInference1.ts, 14, 68))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 18, 16))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 18, 19))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 18, 16))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 18, 19))
|
||||
|
||||
return undefined;
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
function isNonVoid<a>(value: void | a) : value is a {
|
||||
>isNonVoid : Symbol(isNonVoid, Decl(unionAndIntersectionInference1.ts, 20, 1))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 22, 19))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 22, 22))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 22, 19))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 22, 22))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 22, 19))
|
||||
|
||||
return undefined;
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
function foo1<a>(value: void|a): void {
|
||||
>foo1 : Symbol(foo1, Decl(unionAndIntersectionInference1.ts, 24, 1))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 26, 14))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 26, 17))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 26, 14))
|
||||
|
||||
if (isVoid(value)) {
|
||||
>isVoid : Symbol(isVoid, Decl(unionAndIntersectionInference1.ts, 14, 68))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 26, 17))
|
||||
|
||||
value; // value is void
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 26, 17))
|
||||
|
||||
} else {
|
||||
value; // value is a
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 26, 17))
|
||||
}
|
||||
}
|
||||
|
||||
function baz1<a>(value: void|a): void {
|
||||
>baz1 : Symbol(baz1, Decl(unionAndIntersectionInference1.ts, 32, 1))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 34, 14))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 34, 17))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference1.ts, 34, 14))
|
||||
|
||||
if (isNonVoid(value)) {
|
||||
>isNonVoid : Symbol(isNonVoid, Decl(unionAndIntersectionInference1.ts, 20, 1))
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 34, 17))
|
||||
|
||||
value; // value is a
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 34, 17))
|
||||
|
||||
} else {
|
||||
value; // value is void
|
||||
>value : Symbol(value, Decl(unionAndIntersectionInference1.ts, 34, 17))
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #5417
|
||||
|
||||
type Maybe<T> = T | void;
|
||||
>Maybe : Symbol(Maybe, Decl(unionAndIntersectionInference1.ts, 40, 1))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 44, 11))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 44, 11))
|
||||
|
||||
function get<U>(x: U | void): U {
|
||||
>get : Symbol(get, Decl(unionAndIntersectionInference1.ts, 44, 25))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 46, 13))
|
||||
>x : Symbol(x, Decl(unionAndIntersectionInference1.ts, 46, 16))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 46, 13))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference1.ts, 46, 13))
|
||||
|
||||
return null; // just an example
|
||||
}
|
||||
|
||||
let foo: Maybe<string>;
|
||||
>foo : Symbol(foo, Decl(unionAndIntersectionInference1.ts, 50, 3))
|
||||
>Maybe : Symbol(Maybe, Decl(unionAndIntersectionInference1.ts, 40, 1))
|
||||
|
||||
get(foo).toUpperCase(); // Ok
|
||||
>get(foo).toUpperCase : Symbol(String.toUpperCase, Decl(lib.d.ts, --, --))
|
||||
>get : Symbol(get, Decl(unionAndIntersectionInference1.ts, 44, 25))
|
||||
>foo : Symbol(foo, Decl(unionAndIntersectionInference1.ts, 50, 3))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.d.ts, --, --))
|
||||
|
||||
// Repro from #5456
|
||||
|
||||
interface Man {
|
||||
>Man : Symbol(Man, Decl(unionAndIntersectionInference1.ts, 51, 23))
|
||||
|
||||
walks: boolean;
|
||||
>walks : Symbol(walks, Decl(unionAndIntersectionInference1.ts, 55, 15))
|
||||
}
|
||||
|
||||
interface Bear {
|
||||
>Bear : Symbol(Bear, Decl(unionAndIntersectionInference1.ts, 57, 1))
|
||||
|
||||
roars: boolean;
|
||||
>roars : Symbol(roars, Decl(unionAndIntersectionInference1.ts, 59, 16))
|
||||
}
|
||||
|
||||
interface Pig {
|
||||
>Pig : Symbol(Pig, Decl(unionAndIntersectionInference1.ts, 61, 1))
|
||||
|
||||
oinks: boolean;
|
||||
>oinks : Symbol(oinks, Decl(unionAndIntersectionInference1.ts, 63, 15))
|
||||
}
|
||||
|
||||
declare function pigify<T>(y: T & Bear): T & Pig;
|
||||
>pigify : Symbol(pigify, Decl(unionAndIntersectionInference1.ts, 65, 1))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 67, 24))
|
||||
>y : Symbol(y, Decl(unionAndIntersectionInference1.ts, 67, 27))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 67, 24))
|
||||
>Bear : Symbol(Bear, Decl(unionAndIntersectionInference1.ts, 57, 1))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference1.ts, 67, 24))
|
||||
>Pig : Symbol(Pig, Decl(unionAndIntersectionInference1.ts, 61, 1))
|
||||
|
||||
declare var mbp: Man & Bear;
|
||||
>mbp : Symbol(mbp, Decl(unionAndIntersectionInference1.ts, 68, 11))
|
||||
>Man : Symbol(Man, Decl(unionAndIntersectionInference1.ts, 51, 23))
|
||||
>Bear : Symbol(Bear, Decl(unionAndIntersectionInference1.ts, 57, 1))
|
||||
|
||||
pigify(mbp).oinks; // OK, mbp is treated as Pig
|
||||
>pigify(mbp).oinks : Symbol(Pig.oinks, Decl(unionAndIntersectionInference1.ts, 63, 15))
|
||||
>pigify : Symbol(pigify, Decl(unionAndIntersectionInference1.ts, 65, 1))
|
||||
>mbp : Symbol(mbp, Decl(unionAndIntersectionInference1.ts, 68, 11))
|
||||
>oinks : Symbol(Pig.oinks, Decl(unionAndIntersectionInference1.ts, 63, 15))
|
||||
|
||||
pigify(mbp).walks; // Ok, mbp is treated as Man
|
||||
>pigify(mbp).walks : Symbol(Man.walks, Decl(unionAndIntersectionInference1.ts, 55, 15))
|
||||
>pigify : Symbol(pigify, Decl(unionAndIntersectionInference1.ts, 65, 1))
|
||||
>mbp : Symbol(mbp, Decl(unionAndIntersectionInference1.ts, 68, 11))
|
||||
>walks : Symbol(Man.walks, Decl(unionAndIntersectionInference1.ts, 55, 15))
|
||||
|
||||
226
tests/baselines/reference/unionAndIntersectionInference1.types
Normal file
226
tests/baselines/reference/unionAndIntersectionInference1.types
Normal file
@ -0,0 +1,226 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts ===
|
||||
// Repro from #2264
|
||||
|
||||
interface Y { 'i am a very certain type': Y }
|
||||
>Y : Y
|
||||
>Y : Y
|
||||
|
||||
var y: Y = <Y>undefined;
|
||||
>y : Y
|
||||
>Y : Y
|
||||
><Y>undefined : Y
|
||||
>Y : Y
|
||||
>undefined : undefined
|
||||
|
||||
function destructure<a, r>(
|
||||
>destructure : <a, r>(something: a | Y, haveValue: (value: a) => r, haveY: (value: Y) => r) => r
|
||||
>a : a
|
||||
>r : r
|
||||
|
||||
something: a | Y,
|
||||
>something : a | Y
|
||||
>a : a
|
||||
>Y : Y
|
||||
|
||||
haveValue: (value: a) => r,
|
||||
>haveValue : (value: a) => r
|
||||
>value : a
|
||||
>a : a
|
||||
>r : r
|
||||
|
||||
haveY: (value: Y) => r
|
||||
>haveY : (value: Y) => r
|
||||
>value : Y
|
||||
>Y : Y
|
||||
>r : r
|
||||
|
||||
): r {
|
||||
>r : r
|
||||
|
||||
return something === y ? haveY(y) : haveValue(<a>something);
|
||||
>something === y ? haveY(y) : haveValue(<a>something) : r
|
||||
>something === y : boolean
|
||||
>something : a | Y
|
||||
>y : Y
|
||||
>haveY(y) : r
|
||||
>haveY : (value: Y) => r
|
||||
>y : Y
|
||||
>haveValue(<a>something) : r
|
||||
>haveValue : (value: a) => r
|
||||
><a>something : a
|
||||
>a : a
|
||||
>something : a | Y
|
||||
}
|
||||
|
||||
var value = Math.random() > 0.5 ? 'hey!' : <Y>undefined;
|
||||
>value : string | Y
|
||||
>Math.random() > 0.5 ? 'hey!' : <Y>undefined : string | Y
|
||||
>Math.random() > 0.5 : boolean
|
||||
>Math.random() : number
|
||||
>Math.random : () => number
|
||||
>Math : Math
|
||||
>random : () => number
|
||||
>0.5 : number
|
||||
>'hey!' : string
|
||||
><Y>undefined : Y
|
||||
>Y : Y
|
||||
>undefined : undefined
|
||||
|
||||
var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y
|
||||
>result : string
|
||||
>destructure(value, text => 'string', y => 'other one') : string
|
||||
>destructure : <a, r>(something: a | Y, haveValue: (value: a) => r, haveY: (value: Y) => r) => r
|
||||
>value : string | Y
|
||||
>text => 'string' : (text: string) => string
|
||||
>text : string
|
||||
>'string' : string
|
||||
>y => 'other one' : (y: Y) => string
|
||||
>y : Y
|
||||
>'other one' : string
|
||||
|
||||
// Repro from #4212
|
||||
|
||||
function isVoid<a>(value: void | a): value is void {
|
||||
>isVoid : <a>(value: void | a) => value is void
|
||||
>a : a
|
||||
>value : void | a
|
||||
>a : a
|
||||
>value : any
|
||||
|
||||
return undefined;
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
function isNonVoid<a>(value: void | a) : value is a {
|
||||
>isNonVoid : <a>(value: void | a) => value is a
|
||||
>a : a
|
||||
>value : void | a
|
||||
>a : a
|
||||
>value : any
|
||||
>a : a
|
||||
|
||||
return undefined;
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
function foo1<a>(value: void|a): void {
|
||||
>foo1 : <a>(value: void | a) => void
|
||||
>a : a
|
||||
>value : void | a
|
||||
>a : a
|
||||
|
||||
if (isVoid(value)) {
|
||||
>isVoid(value) : boolean
|
||||
>isVoid : <a>(value: void | a) => value is void
|
||||
>value : void | a
|
||||
|
||||
value; // value is void
|
||||
>value : void
|
||||
|
||||
} else {
|
||||
value; // value is a
|
||||
>value : a
|
||||
}
|
||||
}
|
||||
|
||||
function baz1<a>(value: void|a): void {
|
||||
>baz1 : <a>(value: void | a) => void
|
||||
>a : a
|
||||
>value : void | a
|
||||
>a : a
|
||||
|
||||
if (isNonVoid(value)) {
|
||||
>isNonVoid(value) : boolean
|
||||
>isNonVoid : <a>(value: void | a) => value is a
|
||||
>value : void | a
|
||||
|
||||
value; // value is a
|
||||
>value : a
|
||||
|
||||
} else {
|
||||
value; // value is void
|
||||
>value : void
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #5417
|
||||
|
||||
type Maybe<T> = T | void;
|
||||
>Maybe : T | void
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
function get<U>(x: U | void): U {
|
||||
>get : <U>(x: U | void) => U
|
||||
>U : U
|
||||
>x : U | void
|
||||
>U : U
|
||||
>U : U
|
||||
|
||||
return null; // just an example
|
||||
>null : null
|
||||
}
|
||||
|
||||
let foo: Maybe<string>;
|
||||
>foo : string | void
|
||||
>Maybe : T | void
|
||||
|
||||
get(foo).toUpperCase(); // Ok
|
||||
>get(foo).toUpperCase() : string
|
||||
>get(foo).toUpperCase : () => string
|
||||
>get(foo) : string
|
||||
>get : <U>(x: U | void) => U
|
||||
>foo : string | void
|
||||
>toUpperCase : () => string
|
||||
|
||||
// Repro from #5456
|
||||
|
||||
interface Man {
|
||||
>Man : Man
|
||||
|
||||
walks: boolean;
|
||||
>walks : boolean
|
||||
}
|
||||
|
||||
interface Bear {
|
||||
>Bear : Bear
|
||||
|
||||
roars: boolean;
|
||||
>roars : boolean
|
||||
}
|
||||
|
||||
interface Pig {
|
||||
>Pig : Pig
|
||||
|
||||
oinks: boolean;
|
||||
>oinks : boolean
|
||||
}
|
||||
|
||||
declare function pigify<T>(y: T & Bear): T & Pig;
|
||||
>pigify : <T>(y: T & Bear) => T & Pig
|
||||
>T : T
|
||||
>y : T & Bear
|
||||
>T : T
|
||||
>Bear : Bear
|
||||
>T : T
|
||||
>Pig : Pig
|
||||
|
||||
declare var mbp: Man & Bear;
|
||||
>mbp : Man & Bear
|
||||
>Man : Man
|
||||
>Bear : Bear
|
||||
|
||||
pigify(mbp).oinks; // OK, mbp is treated as Pig
|
||||
>pigify(mbp).oinks : boolean
|
||||
>pigify(mbp) : Man & Pig
|
||||
>pigify : <T>(y: T & Bear) => T & Pig
|
||||
>mbp : Man & Bear
|
||||
>oinks : boolean
|
||||
|
||||
pigify(mbp).walks; // Ok, mbp is treated as Man
|
||||
>pigify(mbp).walks : boolean
|
||||
>pigify(mbp) : Man & Pig
|
||||
>pigify : <T>(y: T & Bear) => T & Pig
|
||||
>mbp : Man & Bear
|
||||
>walks : boolean
|
||||
|
||||
45
tests/baselines/reference/unionAndIntersectionInference2.js
Normal file
45
tests/baselines/reference/unionAndIntersectionInference2.js
Normal file
@ -0,0 +1,45 @@
|
||||
//// [unionAndIntersectionInference2.ts]
|
||||
declare function f1<T>(x: T | string): T;
|
||||
|
||||
var a1: string;
|
||||
var b1: string | string[];
|
||||
var c1: string[] | string;
|
||||
var d1: string | { name: string };
|
||||
var e1: number | string | boolean;
|
||||
f1(a1); // string
|
||||
f1(b1); // string[]
|
||||
f1(c1); // string[]
|
||||
f1(d1); // { name: string }
|
||||
f1(e1); // number | boolean
|
||||
|
||||
declare function f2<T>(x: T & { name: string }): T;
|
||||
|
||||
var a2: string & { name: string };
|
||||
var b2: { name: string } & string[];
|
||||
var c2: string & { name: string } & number;
|
||||
var d2: string & { name: string } & number & { name: string };
|
||||
f2(a2); // string
|
||||
f2(b2); // string[]
|
||||
f2(c2); // string & number
|
||||
f2(d2); // string & number
|
||||
|
||||
|
||||
//// [unionAndIntersectionInference2.js]
|
||||
var a1;
|
||||
var b1;
|
||||
var c1;
|
||||
var d1;
|
||||
var e1;
|
||||
f1(a1); // string
|
||||
f1(b1); // string[]
|
||||
f1(c1); // string[]
|
||||
f1(d1); // { name: string }
|
||||
f1(e1); // number | boolean
|
||||
var a2;
|
||||
var b2;
|
||||
var c2;
|
||||
var d2;
|
||||
f2(a2); // string
|
||||
f2(b2); // string[]
|
||||
f2(c2); // string & number
|
||||
f2(d2); // string & number
|
||||
@ -0,0 +1,85 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts ===
|
||||
declare function f1<T>(x: T | string): T;
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 0, 20))
|
||||
>x : Symbol(x, Decl(unionAndIntersectionInference2.ts, 0, 23))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 0, 20))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 0, 20))
|
||||
|
||||
var a1: string;
|
||||
>a1 : Symbol(a1, Decl(unionAndIntersectionInference2.ts, 2, 3))
|
||||
|
||||
var b1: string | string[];
|
||||
>b1 : Symbol(b1, Decl(unionAndIntersectionInference2.ts, 3, 3))
|
||||
|
||||
var c1: string[] | string;
|
||||
>c1 : Symbol(c1, Decl(unionAndIntersectionInference2.ts, 4, 3))
|
||||
|
||||
var d1: string | { name: string };
|
||||
>d1 : Symbol(d1, Decl(unionAndIntersectionInference2.ts, 5, 3))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 5, 18))
|
||||
|
||||
var e1: number | string | boolean;
|
||||
>e1 : Symbol(e1, Decl(unionAndIntersectionInference2.ts, 6, 3))
|
||||
|
||||
f1(a1); // string
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>a1 : Symbol(a1, Decl(unionAndIntersectionInference2.ts, 2, 3))
|
||||
|
||||
f1(b1); // string[]
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>b1 : Symbol(b1, Decl(unionAndIntersectionInference2.ts, 3, 3))
|
||||
|
||||
f1(c1); // string[]
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>c1 : Symbol(c1, Decl(unionAndIntersectionInference2.ts, 4, 3))
|
||||
|
||||
f1(d1); // { name: string }
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>d1 : Symbol(d1, Decl(unionAndIntersectionInference2.ts, 5, 3))
|
||||
|
||||
f1(e1); // number | boolean
|
||||
>f1 : Symbol(f1, Decl(unionAndIntersectionInference2.ts, 0, 0))
|
||||
>e1 : Symbol(e1, Decl(unionAndIntersectionInference2.ts, 6, 3))
|
||||
|
||||
declare function f2<T>(x: T & { name: string }): T;
|
||||
>f2 : Symbol(f2, Decl(unionAndIntersectionInference2.ts, 11, 7))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 13, 20))
|
||||
>x : Symbol(x, Decl(unionAndIntersectionInference2.ts, 13, 23))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 13, 20))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 13, 31))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference2.ts, 13, 20))
|
||||
|
||||
var a2: string & { name: string };
|
||||
>a2 : Symbol(a2, Decl(unionAndIntersectionInference2.ts, 15, 3))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 15, 18))
|
||||
|
||||
var b2: { name: string } & string[];
|
||||
>b2 : Symbol(b2, Decl(unionAndIntersectionInference2.ts, 16, 3))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 16, 9))
|
||||
|
||||
var c2: string & { name: string } & number;
|
||||
>c2 : Symbol(c2, Decl(unionAndIntersectionInference2.ts, 17, 3))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 17, 18))
|
||||
|
||||
var d2: string & { name: string } & number & { name: string };
|
||||
>d2 : Symbol(d2, Decl(unionAndIntersectionInference2.ts, 18, 3))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 18, 18))
|
||||
>name : Symbol(name, Decl(unionAndIntersectionInference2.ts, 18, 46))
|
||||
|
||||
f2(a2); // string
|
||||
>f2 : Symbol(f2, Decl(unionAndIntersectionInference2.ts, 11, 7))
|
||||
>a2 : Symbol(a2, Decl(unionAndIntersectionInference2.ts, 15, 3))
|
||||
|
||||
f2(b2); // string[]
|
||||
>f2 : Symbol(f2, Decl(unionAndIntersectionInference2.ts, 11, 7))
|
||||
>b2 : Symbol(b2, Decl(unionAndIntersectionInference2.ts, 16, 3))
|
||||
|
||||
f2(c2); // string & number
|
||||
>f2 : Symbol(f2, Decl(unionAndIntersectionInference2.ts, 11, 7))
|
||||
>c2 : Symbol(c2, Decl(unionAndIntersectionInference2.ts, 17, 3))
|
||||
|
||||
f2(d2); // string & number
|
||||
>f2 : Symbol(f2, Decl(unionAndIntersectionInference2.ts, 11, 7))
|
||||
>d2 : Symbol(d2, Decl(unionAndIntersectionInference2.ts, 18, 3))
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts ===
|
||||
declare function f1<T>(x: T | string): T;
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>T : T
|
||||
>x : T | string
|
||||
>T : T
|
||||
>T : T
|
||||
|
||||
var a1: string;
|
||||
>a1 : string
|
||||
|
||||
var b1: string | string[];
|
||||
>b1 : string | string[]
|
||||
|
||||
var c1: string[] | string;
|
||||
>c1 : string[] | string
|
||||
|
||||
var d1: string | { name: string };
|
||||
>d1 : string | { name: string; }
|
||||
>name : string
|
||||
|
||||
var e1: number | string | boolean;
|
||||
>e1 : number | string | boolean
|
||||
|
||||
f1(a1); // string
|
||||
>f1(a1) : string
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>a1 : string
|
||||
|
||||
f1(b1); // string[]
|
||||
>f1(b1) : string[]
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>b1 : string | string[]
|
||||
|
||||
f1(c1); // string[]
|
||||
>f1(c1) : string[]
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>c1 : string[] | string
|
||||
|
||||
f1(d1); // { name: string }
|
||||
>f1(d1) : { name: string; }
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>d1 : string | { name: string; }
|
||||
|
||||
f1(e1); // number | boolean
|
||||
>f1(e1) : number | boolean
|
||||
>f1 : <T>(x: T | string) => T
|
||||
>e1 : number | string | boolean
|
||||
|
||||
declare function f2<T>(x: T & { name: string }): T;
|
||||
>f2 : <T>(x: T & { name: string; }) => T
|
||||
>T : T
|
||||
>x : T & { name: string; }
|
||||
>T : T
|
||||
>name : string
|
||||
>T : T
|
||||
|
||||
var a2: string & { name: string };
|
||||
>a2 : string & { name: string; }
|
||||
>name : string
|
||||
|
||||
var b2: { name: string } & string[];
|
||||
>b2 : { name: string; } & string[]
|
||||
>name : string
|
||||
|
||||
var c2: string & { name: string } & number;
|
||||
>c2 : string & { name: string; } & number
|
||||
>name : string
|
||||
|
||||
var d2: string & { name: string } & number & { name: string };
|
||||
>d2 : string & { name: string; } & number & { name: string; }
|
||||
>name : string
|
||||
>name : string
|
||||
|
||||
f2(a2); // string
|
||||
>f2(a2) : string
|
||||
>f2 : <T>(x: T & { name: string; }) => T
|
||||
>a2 : string & { name: string; }
|
||||
|
||||
f2(b2); // string[]
|
||||
>f2(b2) : string[]
|
||||
>f2 : <T>(x: T & { name: string; }) => T
|
||||
>b2 : { name: string; } & string[]
|
||||
|
||||
f2(c2); // string & number
|
||||
>f2(c2) : string & number
|
||||
>f2 : <T>(x: T & { name: string; }) => T
|
||||
>c2 : string & { name: string; } & number
|
||||
|
||||
f2(d2); // string & number
|
||||
>f2(d2) : string & number
|
||||
>f2 : <T>(x: T & { name: string; }) => T
|
||||
>d2 : string & { name: string; } & number & { name: string; }
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
// Repro from #2264
|
||||
|
||||
interface Y { 'i am a very certain type': Y }
|
||||
var y: Y = <Y>undefined;
|
||||
function destructure<a, r>(
|
||||
something: a | Y,
|
||||
haveValue: (value: a) => r,
|
||||
haveY: (value: Y) => r
|
||||
): r {
|
||||
return something === y ? haveY(y) : haveValue(<a>something);
|
||||
}
|
||||
|
||||
var value = Math.random() > 0.5 ? 'hey!' : <Y>undefined;
|
||||
|
||||
var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y
|
||||
|
||||
// Repro from #4212
|
||||
|
||||
function isVoid<a>(value: void | a): value is void {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isNonVoid<a>(value: void | a) : value is a {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function foo1<a>(value: void|a): void {
|
||||
if (isVoid(value)) {
|
||||
value; // value is void
|
||||
} else {
|
||||
value; // value is a
|
||||
}
|
||||
}
|
||||
|
||||
function baz1<a>(value: void|a): void {
|
||||
if (isNonVoid(value)) {
|
||||
value; // value is a
|
||||
} else {
|
||||
value; // value is void
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #5417
|
||||
|
||||
type Maybe<T> = T | void;
|
||||
|
||||
function get<U>(x: U | void): U {
|
||||
return null; // just an example
|
||||
}
|
||||
|
||||
let foo: Maybe<string>;
|
||||
get(foo).toUpperCase(); // Ok
|
||||
|
||||
// Repro from #5456
|
||||
|
||||
interface Man {
|
||||
walks: boolean;
|
||||
}
|
||||
|
||||
interface Bear {
|
||||
roars: boolean;
|
||||
}
|
||||
|
||||
interface Pig {
|
||||
oinks: boolean;
|
||||
}
|
||||
|
||||
declare function pigify<T>(y: T & Bear): T & Pig;
|
||||
declare var mbp: Man & Bear;
|
||||
|
||||
pigify(mbp).oinks; // OK, mbp is treated as Pig
|
||||
pigify(mbp).walks; // Ok, mbp is treated as Man
|
||||
@ -0,0 +1,23 @@
|
||||
declare function f1<T>(x: T | string): T;
|
||||
|
||||
var a1: string;
|
||||
var b1: string | string[];
|
||||
var c1: string[] | string;
|
||||
var d1: string | { name: string };
|
||||
var e1: number | string | boolean;
|
||||
f1(a1); // string
|
||||
f1(b1); // string[]
|
||||
f1(c1); // string[]
|
||||
f1(d1); // { name: string }
|
||||
f1(e1); // number | boolean
|
||||
|
||||
declare function f2<T>(x: T & { name: string }): T;
|
||||
|
||||
var a2: string & { name: string };
|
||||
var b2: { name: string } & string[];
|
||||
var c2: string & { name: string } & number;
|
||||
var d2: string & { name: string } & number & { name: string };
|
||||
f2(a2); // string
|
||||
f2(b2); // string[]
|
||||
f2(c2); // string & number
|
||||
f2(d2); // string & number
|
||||
Loading…
x
Reference in New Issue
Block a user