diff --git a/package.json b/package.json index 401f9d7771dd3..1d6e55bb00523 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "browserify": "latest", "istanbul": "latest", "mocha-fivemat-progress-reporter": "latest", - "tslint": "latest", + "tslint": "~2", "tsd": "latest" }, "scripts": { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1273fd50a4a20..bdafab7161db5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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; @@ -4938,9 +4940,6 @@ namespace ts { } return objectTypeRelatedTo(source, target, /*reportErrors*/ false); } - if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) { - return typeParameterIdenticalTo(source, target); - } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { if (result = eachTypeRelatedToSomeType(source, target)) { @@ -5071,20 +5070,6 @@ namespace ts { return result; } - function typeParameterIdenticalTo(source: TypeParameter, target: TypeParameter): Ternary { - if (source.symbol.name !== target.symbol.name) { - return Ternary.False; - } - // covers case when both type parameters does not have constraint (both equal to noConstraintType) - if (source.constraint === target.constraint) { - return Ternary.True; - } - if (source.constraint === noConstraintType || target.constraint === noConstraintType) { - return Ternary.False; - } - return isIdenticalTo(source.constraint, target.constraint); - } - // Determine if two object types are related by structure. First, check if the result is already available in the global cache. // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are @@ -5607,27 +5592,20 @@ namespace ts { return Ternary.False; } } - let result = Ternary.True; - if (source.typeParameters && target.typeParameters) { - if (source.typeParameters.length !== target.typeParameters.length) { - return Ternary.False; - } - for (let i = 0, len = source.typeParameters.length; i < len; ++i) { - let related = compareTypes(source.typeParameters[i], target.typeParameters[i]); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - else if (source.typeParameters || target.typeParameters) { + // Check that the two signatures have the same number of type parameters. We might consider + // also checking that any type parameter constraints match, but that would require instantiating + // the constraints with a common set of type arguments to get relatable entities in places where + // type parameters occur in the constraints. The complexity of doing that doesn't seem worthwhile, + // particularly as we're comparing erased versions of the signatures below. + if ((source.typeParameters ? source.typeParameters.length : 0) !== (target.typeParameters ? target.typeParameters.length : 0)) { return Ternary.False; } // Spec 1.0 Section 3.8.3 & 3.8.4: // M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N source = getErasedSignature(source); target = getErasedSignature(target); - let targetLen = target.parameters.length; + let result = Ternary.True; + const targetLen = target.parameters.length; for (let i = 0; i < targetLen; i++) { let s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]); let t = isRestParameterIndex(target, i) ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]); @@ -5928,6 +5906,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(source, target); + const reducedTarget = reduceUnionOrIntersectionType(target, 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 +5927,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 +6074,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 +7882,6 @@ namespace ts { return prop || unknownSymbol; } - let jsxElementClassType: Type = undefined; function getJsxGlobalElementClassType(): Type { if (!jsxElementClassType) { jsxElementClassType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.ElementClass); diff --git a/tests/baselines/reference/genericSignatureIdentity.js b/tests/baselines/reference/genericSignatureIdentity.js new file mode 100644 index 0000000000000..9a51a61edbd53 --- /dev/null +++ b/tests/baselines/reference/genericSignatureIdentity.js @@ -0,0 +1,32 @@ +//// [genericSignatureIdentity.ts] +// This test is here to remind us of our current limits of type identity checking. +// Ideally all of the below declarations would be considered different (and thus errors) +// but they aren't because we erase type parameters to type any and don't check that +// constraints are identical. + +var x: { + (x: T): T; +}; + +var x: { + (x: T): T; +}; + +var x: { + (x: T): T; +}; + +var x: { + (x: any): any; +}; + + +//// [genericSignatureIdentity.js] +// This test is here to remind us of our current limits of type identity checking. +// Ideally all of the below declarations would be considered different (and thus errors) +// but they aren't because we erase type parameters to type any and don't check that +// constraints are identical. +var x; +var x; +var x; +var x; diff --git a/tests/baselines/reference/genericSignatureIdentity.symbols b/tests/baselines/reference/genericSignatureIdentity.symbols new file mode 100644 index 0000000000000..afd12ec266a18 --- /dev/null +++ b/tests/baselines/reference/genericSignatureIdentity.symbols @@ -0,0 +1,49 @@ +=== tests/cases/compiler/genericSignatureIdentity.ts === +// This test is here to remind us of our current limits of type identity checking. +// Ideally all of the below declarations would be considered different (and thus errors) +// but they aren't because we erase type parameters to type any and don't check that +// constraints are identical. + +var x: { +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3)) + + (x: T): T; +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 6, 21)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5)) + +}; + +var x: { +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3)) + + (x: T): T; +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5)) +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 10, 23)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5)) + +}; + +var x: { +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3)) + + (x: T): T; +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5)) +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 14, 8)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5)) +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5)) + +}; + +var x: { +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3)) + + (x: any): any; +>T : Symbol(T, Decl(genericSignatureIdentity.ts, 18, 5)) +>x : Symbol(x, Decl(genericSignatureIdentity.ts, 18, 8)) + +}; + diff --git a/tests/baselines/reference/genericSignatureIdentity.types b/tests/baselines/reference/genericSignatureIdentity.types new file mode 100644 index 0000000000000..9385718a36127 --- /dev/null +++ b/tests/baselines/reference/genericSignatureIdentity.types @@ -0,0 +1,49 @@ +=== tests/cases/compiler/genericSignatureIdentity.ts === +// This test is here to remind us of our current limits of type identity checking. +// Ideally all of the below declarations would be considered different (and thus errors) +// but they aren't because we erase type parameters to type any and don't check that +// constraints are identical. + +var x: { +>x : (x: T) => T + + (x: T): T; +>T : T +>Date : Date +>x : T +>T : T +>T : T + +}; + +var x: { +>x : (x: T) => T + + (x: T): T; +>T : T +>x : T +>T : T +>T : T + +}; + +var x: { +>x : (x: T) => T + + (x: T): T; +>T : T +>x : T +>T : T +>T : T + +}; + +var x: { +>x : (x: T) => T + + (x: any): any; +>T : T +>x : any + +}; + diff --git a/tests/baselines/reference/unionAndIntersectionInference1.js b/tests/baselines/reference/unionAndIntersectionInference1.js new file mode 100644 index 0000000000000..235eb23ebf847 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference1.js @@ -0,0 +1,113 @@ +//// [unionAndIntersectionInference1.ts] +// Repro from #2264 + +interface Y { 'i am a very certain type': Y } +var y: Y = undefined; +function destructure( + something: a | Y, + haveValue: (value: a) => r, + haveY: (value: Y) => r +): r { + return something === y ? haveY(y) : haveValue(something); +} + +var value = Math.random() > 0.5 ? 'hey!' : undefined; + +var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y + +// Repro from #4212 + +function isVoid(value: void | a): value is void { + return undefined; +} + +function isNonVoid(value: void | a) : value is a { + return undefined; +} + +function foo1(value: void|a): void { + if (isVoid(value)) { + value; // value is void + } else { + value; // value is a + } +} + +function baz1(value: void|a): void { + if (isNonVoid(value)) { + value; // value is a + } else { + value; // value is void + } +} + +// Repro from #5417 + +type Maybe = T | void; + +function get(x: U | void): U { + return null; // just an example +} + +let foo: Maybe; +get(foo).toUpperCase(); // Ok + +// Repro from #5456 + +interface Man { + walks: boolean; +} + +interface Bear { + roars: boolean; +} + +interface Pig { + oinks: boolean; +} + +declare function pigify(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 diff --git a/tests/baselines/reference/unionAndIntersectionInference1.symbols b/tests/baselines/reference/unionAndIntersectionInference1.symbols new file mode 100644 index 0000000000000..5e96790ea3470 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference1.symbols @@ -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 = 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( +>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(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!' : 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(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(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(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(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 | 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(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; +>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(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)) + diff --git a/tests/baselines/reference/unionAndIntersectionInference1.types b/tests/baselines/reference/unionAndIntersectionInference1.types new file mode 100644 index 0000000000000..5d23688f0b7c3 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference1.types @@ -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 = undefined; +>y : Y +>Y : Y +>undefined : Y +>Y : Y +>undefined : undefined + +function destructure( +>destructure : (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(something); +>something === y ? haveY(y) : haveValue(something) : r +>something === y : boolean +>something : a | Y +>y : Y +>haveY(y) : r +>haveY : (value: Y) => r +>y : Y +>haveValue(something) : r +>haveValue : (value: a) => r +>something : a +>a : a +>something : a | Y +} + +var value = Math.random() > 0.5 ? 'hey!' : undefined; +>value : string | Y +>Math.random() > 0.5 ? 'hey!' : undefined : string | Y +>Math.random() > 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : number +>'hey!' : string +>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 : (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(value: void | a): value is void { +>isVoid : (value: void | a) => value is void +>a : a +>value : void | a +>a : a +>value : any + + return undefined; +>undefined : undefined +} + +function isNonVoid(value: void | a) : value is a { +>isNonVoid : (value: void | a) => value is a +>a : a +>value : void | a +>a : a +>value : any +>a : a + + return undefined; +>undefined : undefined +} + +function foo1(value: void|a): void { +>foo1 : (value: void | a) => void +>a : a +>value : void | a +>a : a + + if (isVoid(value)) { +>isVoid(value) : boolean +>isVoid : (value: void | a) => value is void +>value : void | a + + value; // value is void +>value : void + + } else { + value; // value is a +>value : a + } +} + +function baz1(value: void|a): void { +>baz1 : (value: void | a) => void +>a : a +>value : void | a +>a : a + + if (isNonVoid(value)) { +>isNonVoid(value) : boolean +>isNonVoid : (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 | void; +>Maybe : T | void +>T : T +>T : T + +function get(x: U | void): U { +>get : (x: U | void) => U +>U : U +>x : U | void +>U : U +>U : U + + return null; // just an example +>null : null +} + +let foo: Maybe; +>foo : string | void +>Maybe : T | void + +get(foo).toUpperCase(); // Ok +>get(foo).toUpperCase() : string +>get(foo).toUpperCase : () => string +>get(foo) : string +>get : (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(y: T & Bear): T & Pig; +>pigify : (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 : (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 : (y: T & Bear) => T & Pig +>mbp : Man & Bear +>walks : boolean + diff --git a/tests/baselines/reference/unionAndIntersectionInference2.js b/tests/baselines/reference/unionAndIntersectionInference2.js new file mode 100644 index 0000000000000..18f08452a2ef5 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference2.js @@ -0,0 +1,45 @@ +//// [unionAndIntersectionInference2.ts] +declare function f1(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(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 diff --git a/tests/baselines/reference/unionAndIntersectionInference2.symbols b/tests/baselines/reference/unionAndIntersectionInference2.symbols new file mode 100644 index 0000000000000..24b1a36aa45e6 --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference2.symbols @@ -0,0 +1,85 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts === +declare function f1(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(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)) + diff --git a/tests/baselines/reference/unionAndIntersectionInference2.types b/tests/baselines/reference/unionAndIntersectionInference2.types new file mode 100644 index 0000000000000..beeb2a261f3ec --- /dev/null +++ b/tests/baselines/reference/unionAndIntersectionInference2.types @@ -0,0 +1,94 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts === +declare function f1(x: T | string): T; +>f1 : (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 : (x: T | string) => T +>a1 : string + +f1(b1); // string[] +>f1(b1) : string[] +>f1 : (x: T | string) => T +>b1 : string | string[] + +f1(c1); // string[] +>f1(c1) : string[] +>f1 : (x: T | string) => T +>c1 : string[] | string + +f1(d1); // { name: string } +>f1(d1) : { name: string; } +>f1 : (x: T | string) => T +>d1 : string | { name: string; } + +f1(e1); // number | boolean +>f1(e1) : number | boolean +>f1 : (x: T | string) => T +>e1 : number | string | boolean + +declare function f2(x: T & { name: string }): T; +>f2 : (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 : (x: T & { name: string; }) => T +>a2 : string & { name: string; } + +f2(b2); // string[] +>f2(b2) : string[] +>f2 : (x: T & { name: string; }) => T +>b2 : { name: string; } & string[] + +f2(c2); // string & number +>f2(c2) : string & number +>f2 : (x: T & { name: string; }) => T +>c2 : string & { name: string; } & number + +f2(d2); // string & number +>f2(d2) : string & number +>f2 : (x: T & { name: string; }) => T +>d2 : string & { name: string; } & number & { name: string; } + diff --git a/tests/baselines/reference/unionTypeParameterInference.js b/tests/baselines/reference/unionTypeParameterInference.js new file mode 100644 index 0000000000000..c96e809cd4dc8 --- /dev/null +++ b/tests/baselines/reference/unionTypeParameterInference.js @@ -0,0 +1,17 @@ +//// [unionTypeParameterInference.ts] +// Regression test for #5861 + +interface Foo { prop: T; } + +declare function lift(value: U | Foo): Foo; + +function unlift(value: U | Foo): U { + return lift(value).prop; +} + + +//// [unionTypeParameterInference.js] +// Regression test for #5861 +function unlift(value) { + return lift(value).prop; +} diff --git a/tests/baselines/reference/unionTypeParameterInference.symbols b/tests/baselines/reference/unionTypeParameterInference.symbols new file mode 100644 index 0000000000000..f2dbaac31ffb5 --- /dev/null +++ b/tests/baselines/reference/unionTypeParameterInference.symbols @@ -0,0 +1,35 @@ +=== tests/cases/compiler/unionTypeParameterInference.ts === +// Regression test for #5861 + +interface Foo { prop: T; } +>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0)) +>T : Symbol(T, Decl(unionTypeParameterInference.ts, 2, 14)) +>prop : Symbol(prop, Decl(unionTypeParameterInference.ts, 2, 18)) +>T : Symbol(T, Decl(unionTypeParameterInference.ts, 2, 14)) + +declare function lift(value: U | Foo): Foo; +>lift : Symbol(lift, Decl(unionTypeParameterInference.ts, 2, 29)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22)) +>value : Symbol(value, Decl(unionTypeParameterInference.ts, 4, 25)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22)) +>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22)) +>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22)) + +function unlift(value: U | Foo): U { +>unlift : Symbol(unlift, Decl(unionTypeParameterInference.ts, 4, 52)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16)) +>value : Symbol(value, Decl(unionTypeParameterInference.ts, 6, 19)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16)) +>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16)) +>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16)) + + return lift(value).prop; +>lift(value).prop : Symbol(Foo.prop, Decl(unionTypeParameterInference.ts, 2, 18)) +>lift : Symbol(lift, Decl(unionTypeParameterInference.ts, 2, 29)) +>value : Symbol(value, Decl(unionTypeParameterInference.ts, 6, 19)) +>prop : Symbol(Foo.prop, Decl(unionTypeParameterInference.ts, 2, 18)) +} + diff --git a/tests/baselines/reference/unionTypeParameterInference.types b/tests/baselines/reference/unionTypeParameterInference.types new file mode 100644 index 0000000000000..54eaed90ecc42 --- /dev/null +++ b/tests/baselines/reference/unionTypeParameterInference.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/unionTypeParameterInference.ts === +// Regression test for #5861 + +interface Foo { prop: T; } +>Foo : Foo +>T : T +>prop : T +>T : T + +declare function lift(value: U | Foo): Foo; +>lift : (value: U | Foo) => Foo +>U : U +>value : U | Foo +>U : U +>Foo : Foo +>U : U +>Foo : Foo +>U : U + +function unlift(value: U | Foo): U { +>unlift : (value: U | Foo) => U +>U : U +>value : U | Foo +>U : U +>Foo : Foo +>U : U +>U : U + + return lift(value).prop; +>lift(value).prop : U +>lift(value) : Foo +>lift : (value: U | Foo) => Foo +>value : U | Foo +>prop : U +} + diff --git a/tests/cases/compiler/genericSignatureIdentity.ts b/tests/cases/compiler/genericSignatureIdentity.ts new file mode 100644 index 0000000000000..c685b8cc57363 --- /dev/null +++ b/tests/cases/compiler/genericSignatureIdentity.ts @@ -0,0 +1,20 @@ +// This test is here to remind us of our current limits of type identity checking. +// Ideally all of the below declarations would be considered different (and thus errors) +// but they aren't because we erase type parameters to type any and don't check that +// constraints are identical. + +var x: { + (x: T): T; +}; + +var x: { + (x: T): T; +}; + +var x: { + (x: T): T; +}; + +var x: { + (x: any): any; +}; diff --git a/tests/cases/compiler/unionTypeParameterInference.ts b/tests/cases/compiler/unionTypeParameterInference.ts new file mode 100644 index 0000000000000..79c4f3cc0e52a --- /dev/null +++ b/tests/cases/compiler/unionTypeParameterInference.ts @@ -0,0 +1,9 @@ +// Regression test for #5861 + +interface Foo { prop: T; } + +declare function lift(value: U | Foo): Foo; + +function unlift(value: U | Foo): U { + return lift(value).prop; +} diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts new file mode 100644 index 0000000000000..7066c3e679084 --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference1.ts @@ -0,0 +1,72 @@ +// Repro from #2264 + +interface Y { 'i am a very certain type': Y } +var y: Y = undefined; +function destructure( + something: a | Y, + haveValue: (value: a) => r, + haveY: (value: Y) => r +): r { + return something === y ? haveY(y) : haveValue(something); +} + +var value = Math.random() > 0.5 ? 'hey!' : undefined; + +var result = destructure(value, text => 'string', y => 'other one'); // text: string, y: Y + +// Repro from #4212 + +function isVoid(value: void | a): value is void { + return undefined; +} + +function isNonVoid(value: void | a) : value is a { + return undefined; +} + +function foo1(value: void|a): void { + if (isVoid(value)) { + value; // value is void + } else { + value; // value is a + } +} + +function baz1(value: void|a): void { + if (isNonVoid(value)) { + value; // value is a + } else { + value; // value is void + } +} + +// Repro from #5417 + +type Maybe = T | void; + +function get(x: U | void): U { + return null; // just an example +} + +let foo: Maybe; +get(foo).toUpperCase(); // Ok + +// Repro from #5456 + +interface Man { + walks: boolean; +} + +interface Bear { + roars: boolean; +} + +interface Pig { + oinks: boolean; +} + +declare function pigify(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 diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts new file mode 100644 index 0000000000000..1b6a928ce028c --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference2.ts @@ -0,0 +1,23 @@ +declare function f1(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(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