Fix the logic in getting the contextual signature of union type as per spec

Fixes #1054
This commit is contained in:
Sheetal Nandi 2014-11-04 14:49:15 -08:00
parent c5b4051f3a
commit c4e632746e
6 changed files with 305 additions and 61 deletions

View file

@ -4879,8 +4879,9 @@ module ts {
// Return the contextual signature for a given expression node. A contextual type provides a
// contextual signature if it has a single call signature and if that call signature is non-generic.
// If the contextual type is a union type and each constituent type that has a contextual signature
// provides the same contextual signature, then the union type provides that contextual signature.
// If the contextual type is a union type, get the signature from each type possible and if they are
// all identical ignoring their return type, the result is same signature but with return type as
// union type of return types from these signatures
function getContextualSignature(node: Expression): Signature {
var type = getContextualType(node);
if (!type) {
@ -4889,19 +4890,41 @@ module ts {
if (!(type.flags & TypeFlags.Union)) {
return getNonGenericSignature(type);
}
var result: Signature;
var signatureList: Signature[];
var types = (<UnionType>type).types;
for (var i = 0; i < types.length; i++) {
// The signature set of all constituent type with call signatures should match
// So number of signatures allowed is either 0 or 1
if (signatureList &&
getSignaturesOfObjectOrUnionType(types[i], SignatureKind.Call).length > 1) {
return undefined;
}
var signature = getNonGenericSignature(types[i]);
if (signature) {
if (!result) {
result = signature;
if (!signatureList) {
// This signature will contribute to contextual union signature
signatureList = [signature];
}
else if (!compareSignatures(result, signature, /*compareReturnTypes*/ true, compareTypes)) {
else if (!compareSignatures(signatureList[0], signature, /*compareReturnTypes*/ false, compareTypes)) {
// Signatures arent identical, do not use
return undefined;
}
else {
// Use this signature for contextual union signature
signatureList.push(signature);
}
}
}
// Result is union of signatures collected (return type is union of return types of this signature set)
var result: Signature;
if (signatureList) {
result = cloneSignature(signatureList[0]);
// Clear resolved return type we possibly got from cloneSignature
result.resolvedReturnType = undefined;
result.unionSignatures = signatureList;
}
return result;
}

View file

@ -0,0 +1,86 @@
//// [contextualTypeWithUnionTypeCallSignatures.ts]
//When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
// Let S be the set of types in U that have call signatures.
// If S is not empty and the sets of call signatures of the types in S are identical ignoring return types,
// U has the same set of call signatures, but with return types that are unions of the return types of the respective call signatures from each type in S.
interface IWithNoCallSignatures {
foo: string;
}
interface IWithCallSignatures {
(a: number): string;
}
interface IWithCallSignatures2 {
(a: number): number;
}
interface IWithCallSignatures3 {
(b: string): number;
}
interface IWithCallSignatures4 {
(a: number): string;
(a: string, b: number): number;
}
// With no call signature | callSignatures
var x: IWithNoCallSignatures | IWithCallSignatures = a => a.toString();
// With call signatures with different return type
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a.toString(); // Like iWithCallSignatures
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a; // Like iWithCallSignatures2
// With call signatures of mismatching parameter type
var x3: IWithCallSignatures | IWithCallSignatures3 = a => /*here a should be any*/ a.toString();
// With call signature count mismatch
var x4: IWithCallSignatures | IWithCallSignatures4 = a => /*here a should be any*/ a.toString();
//// [contextualTypeWithUnionTypeCallSignatures.js]
//When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
// With no call signature | callSignatures
var x = function (a) { return a.toString(); };
// With call signatures with different return type
var x2 = function (a) { return a.toString(); }; // Like iWithCallSignatures
var x2 = function (a) { return a; }; // Like iWithCallSignatures2
// With call signatures of mismatching parameter type
var x3 = function (a /*here a should be any*/) { return a.toString(); };
// With call signature count mismatch
var x4 = function (a //When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
// Let S be the set of types in U that have call signatures.
// If S is not empty and the sets of call signatures of the types in S are identical ignoring return types,
// U has the same set of call signatures, but with return types that are unions of the return types of the respective call signatures from each type in S.
interface IWithNoCallSignatures {
foo: string;
}
interface IWithCallSignatures {
(a: number): string;
}
interface IWithCallSignatures2 {
(a: number): number;
}
interface IWithCallSignatures3 {
(b: string): number;
}
interface IWithCallSignatures4 {
(a: number): string;
(a: string, b: number): number;
}
// With no call signature | callSignatures
var x: IWithNoCallSignatures | IWithCallSignatures = a => a.toString();
// With call signatures with different return type
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a.toString(); // Like iWithCallSignatures
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a; // Like iWithCallSignatures2
// With call signatures of mismatching parameter type
var x3: IWithCallSignatures | IWithCallSignatures3 = a => /*here a should be any*/ a.toString();
// With call signature count mismatch
var x4: IWithCallSignatures | IWithCallSignatures4 = a =>
) { return a.toString(); };

View file

@ -0,0 +1,99 @@
=== tests/cases/conformance/types/union/contextualTypeWithUnionTypeCallSignatures.ts ===
//When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
// Let S be the set of types in U that have call signatures.
// If S is not empty and the sets of call signatures of the types in S are identical ignoring return types,
// U has the same set of call signatures, but with return types that are unions of the return types of the respective call signatures from each type in S.
interface IWithNoCallSignatures {
>IWithNoCallSignatures : IWithNoCallSignatures
foo: string;
>foo : string
}
interface IWithCallSignatures {
>IWithCallSignatures : IWithCallSignatures
(a: number): string;
>a : number
}
interface IWithCallSignatures2 {
>IWithCallSignatures2 : IWithCallSignatures2
(a: number): number;
>a : number
}
interface IWithCallSignatures3 {
>IWithCallSignatures3 : IWithCallSignatures3
(b: string): number;
>b : string
}
interface IWithCallSignatures4 {
>IWithCallSignatures4 : IWithCallSignatures4
(a: number): string;
>a : number
(a: string, b: number): number;
>a : string
>b : number
}
// With no call signature | callSignatures
var x: IWithNoCallSignatures | IWithCallSignatures = a => a.toString();
>x : IWithNoCallSignatures | IWithCallSignatures
>IWithNoCallSignatures : IWithNoCallSignatures
>IWithCallSignatures : IWithCallSignatures
>a => a.toString() : (a: number) => string
>a : number
>a.toString() : string
>a.toString : (radix?: number) => string
>a : number
>toString : (radix?: number) => string
// With call signatures with different return type
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a.toString(); // Like iWithCallSignatures
>x2 : IWithCallSignatures | IWithCallSignatures2
>IWithCallSignatures : IWithCallSignatures
>IWithCallSignatures2 : IWithCallSignatures2
>a => a.toString() : (a: number) => string
>a : number
>a.toString() : string
>a.toString : (radix?: number) => string
>a : number
>toString : (radix?: number) => string
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a; // Like iWithCallSignatures2
>x2 : IWithCallSignatures | IWithCallSignatures2
>IWithCallSignatures : IWithCallSignatures
>IWithCallSignatures2 : IWithCallSignatures2
>a => a : (a: number) => number
>a : number
>a : number
// With call signatures of mismatching parameter type
var x3: IWithCallSignatures | IWithCallSignatures3 = a => /*here a should be any*/ a.toString();
>x3 : IWithCallSignatures | IWithCallSignatures3
>IWithCallSignatures : IWithCallSignatures
>IWithCallSignatures3 : IWithCallSignatures3
>a => /*here a should be any*/ a.toString() : (a: any) => any
>a : any
>a.toString() : any
>a.toString : any
>a : any
>toString : any
// With call signature count mismatch
var x4: IWithCallSignatures | IWithCallSignatures4 = a => /*here a should be any*/ a.toString();
>x4 : IWithCallSignatures | IWithCallSignatures4
>IWithCallSignatures : IWithCallSignatures
>IWithCallSignatures4 : IWithCallSignatures4
>a => /*here a should be any*/ a.toString() : (a: any) => any
>a : any
>a.toString() : any
>a.toString : any
>a : any
>toString : any

View file

@ -321,25 +321,25 @@ var i11Ori21: I11 | I21 = {
>i11Ori21 : I11 | I21
>I11 : I11
>I21 : I21
>{ // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", } : { commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: string; }
>{ // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", } : { commonMethodDifferentReturnType: (a: string, b: number) => string; commonPropertyDifferentType: string; }
// Like i1
commonMethodDifferentReturnType: (a, b) => {
>commonMethodDifferentReturnType : (a: any, b: any) => any
>(a, b) => { var z = a.charAt(b); return z; } : (a: any, b: any) => any
>a : any
>b : any
>commonMethodDifferentReturnType : (a: string, b: number) => string
>(a, b) => { var z = a.charAt(b); return z; } : (a: string, b: number) => string
>a : string
>b : number
var z = a.charAt(b);
>z : any
>a.charAt(b) : any
>a.charAt : any
>a : any
>charAt : any
>b : any
>z : string
>a.charAt(b) : string
>a.charAt : (pos: number) => string
>a : string
>charAt : (pos: number) => string
>b : number
return z;
>z : any
>z : string
},
commonPropertyDifferentType: "hello",
@ -350,25 +350,25 @@ var i11Ori21: I11 | I21 = {
>i11Ori21 : I11 | I21
>I11 : I11
>I21 : I21
>{ // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10,} : { commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: number; }
>{ // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10,} : { commonMethodDifferentReturnType: (a: string, b: number) => number; commonPropertyDifferentType: number; }
// Like i2
commonMethodDifferentReturnType: (a, b) => {
>commonMethodDifferentReturnType : (a: any, b: any) => any
>(a, b) => { var z = a.charCodeAt(b); return z; } : (a: any, b: any) => any
>a : any
>b : any
>commonMethodDifferentReturnType : (a: string, b: number) => number
>(a, b) => { var z = a.charCodeAt(b); return z; } : (a: string, b: number) => number
>a : string
>b : number
var z = a.charCodeAt(b);
>z : any
>a.charCodeAt(b) : any
>a.charCodeAt : any
>a : any
>charCodeAt : any
>b : any
>z : number
>a.charCodeAt(b) : number
>a.charCodeAt : (index: number) => number
>a : string
>charCodeAt : (index: number) => number
>b : number
return z;
>z : any
>z : number
},
commonPropertyDifferentType: 10,
@ -380,56 +380,56 @@ var arrayOrI11OrI21: Array<I11 | I21> = [i11, i21, i11 || i21, {
>Array : T[]
>I11 : I11
>I21 : I21
>[i11, i21, i11 || i21, { // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", }, { // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10, }] : ({ commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: string; } | { commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: number; })[]
>[i11, i21, i11 || i21, { // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", }, { // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10, }] : (I11 | I21)[]
>i11 : I11
>i21 : I21
>i11 || i21 : I11 | I21
>i11 : I11
>i21 : I21
>{ // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", } : { commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: string; }
>{ // Like i1 commonMethodDifferentReturnType: (a, b) => { var z = a.charAt(b); return z; }, commonPropertyDifferentType: "hello", } : { commonMethodDifferentReturnType: (a: string, b: number) => string; commonPropertyDifferentType: string; }
// Like i1
commonMethodDifferentReturnType: (a, b) => {
>commonMethodDifferentReturnType : (a: any, b: any) => any
>(a, b) => { var z = a.charAt(b); return z; } : (a: any, b: any) => any
>a : any
>b : any
>commonMethodDifferentReturnType : (a: string, b: number) => string
>(a, b) => { var z = a.charAt(b); return z; } : (a: string, b: number) => string
>a : string
>b : number
var z = a.charAt(b);
>z : any
>a.charAt(b) : any
>a.charAt : any
>a : any
>charAt : any
>b : any
>z : string
>a.charAt(b) : string
>a.charAt : (pos: number) => string
>a : string
>charAt : (pos: number) => string
>b : number
return z;
>z : any
>z : string
},
commonPropertyDifferentType: "hello",
>commonPropertyDifferentType : string
}, {
>{ // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10, } : { commonMethodDifferentReturnType: (a: any, b: any) => any; commonPropertyDifferentType: number; }
>{ // Like i2 commonMethodDifferentReturnType: (a, b) => { var z = a.charCodeAt(b); return z; }, commonPropertyDifferentType: 10, } : { commonMethodDifferentReturnType: (a: string, b: number) => number; commonPropertyDifferentType: number; }
// Like i2
commonMethodDifferentReturnType: (a, b) => {
>commonMethodDifferentReturnType : (a: any, b: any) => any
>(a, b) => { var z = a.charCodeAt(b); return z; } : (a: any, b: any) => any
>a : any
>b : any
>commonMethodDifferentReturnType : (a: string, b: number) => number
>(a, b) => { var z = a.charCodeAt(b); return z; } : (a: string, b: number) => number
>a : string
>b : number
var z = a.charCodeAt(b);
>z : any
>a.charCodeAt(b) : any
>a.charCodeAt : any
>a : any
>charCodeAt : any
>b : any
>z : number
>a.charCodeAt(b) : number
>a.charCodeAt : (index: number) => number
>a : string
>charCodeAt : (index: number) => number
>b : number
return z;
>z : any
>z : number
},
commonPropertyDifferentType: 10,

View file

@ -23,10 +23,10 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
Types of property 'prop' are incompatible.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(57,5): error TS2323: Type '{ commonMethodDifferentReturnType: (a: any, b: any) => string | number; }' is not assignable to type 'I11 | I21'.
Type '{ commonMethodDifferentReturnType: (a: any, b: any) => string | number; }' is not assignable to type 'I21'.
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(57,5): error TS2323: Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I11 | I21'.
Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I21'.
Types of property 'commonMethodDifferentReturnType' are incompatible.
Type '(a: any, b: any) => string | number' is not assignable to type '(a: string, b: number) => number'.
Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => number'.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
@ -120,10 +120,10 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
var strOrNumber: string | number;
var i11Ori21: I11 | I21 = { // Like i1 and i2 both
~~~~~~~~
!!! error TS2323: Type '{ commonMethodDifferentReturnType: (a: any, b: any) => string | number; }' is not assignable to type 'I11 | I21'.
!!! error TS2323: Type '{ commonMethodDifferentReturnType: (a: any, b: any) => string | number; }' is not assignable to type 'I21'.
!!! error TS2323: Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I11 | I21'.
!!! error TS2323: Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I21'.
!!! error TS2323: Types of property 'commonMethodDifferentReturnType' are incompatible.
!!! error TS2323: Type '(a: any, b: any) => string | number' is not assignable to type '(a: string, b: number) => number'.
!!! error TS2323: Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => number'.
!!! error TS2323: Type 'string | number' is not assignable to type 'number'.
!!! error TS2323: Type 'string' is not assignable to type 'number'.
commonMethodDifferentReturnType: (a, b) => strOrNumber,

View file

@ -0,0 +1,36 @@
//When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
// Let S be the set of types in U that have call signatures.
// If S is not empty and the sets of call signatures of the types in S are identical ignoring return types,
// U has the same set of call signatures, but with return types that are unions of the return types of the respective call signatures from each type in S.
interface IWithNoCallSignatures {
foo: string;
}
interface IWithCallSignatures {
(a: number): string;
}
interface IWithCallSignatures2 {
(a: number): number;
}
interface IWithCallSignatures3 {
(b: string): number;
}
interface IWithCallSignatures4 {
(a: number): string;
(a: string, b: number): number;
}
// With no call signature | callSignatures
var x: IWithNoCallSignatures | IWithCallSignatures = a => a.toString();
// With call signatures with different return type
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a.toString(); // Like iWithCallSignatures
var x2: IWithCallSignatures | IWithCallSignatures2 = a => a; // Like iWithCallSignatures2
// With call signatures of mismatching parameter type
var x3: IWithCallSignatures | IWithCallSignatures3 = a => /*here a should be any*/ a.toString();
// With call signature count mismatch
var x4: IWithCallSignatures | IWithCallSignatures4 = a => /*here a should be any*/ a.toString();