mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 20:37:00 -05:00
Contextual typing for computed properties
This commit is contained in:
@@ -5165,13 +5165,26 @@ module ts {
|
||||
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElement) {
|
||||
var objectLiteral = <ObjectLiteralExpression>element.parent;
|
||||
var type = getContextualType(objectLiteral);
|
||||
// TODO(jfreeman): Handle this case for computed names and symbols
|
||||
var name = (<Identifier>element.name).text;
|
||||
if (type && name) {
|
||||
return getTypeOfPropertyOfContextualType(type, name) ||
|
||||
isNumericName(name) && getIndexTypeOfContextualType(type, IndexKind.Number) ||
|
||||
if (type) {
|
||||
if (!hasComputedNameButNotSymbol(element)) {
|
||||
// For a (non-symbol) computed property, there is no reason to look up the name
|
||||
// in the type. It will just be "__computed", which does not appear in any
|
||||
// SymbolTable.
|
||||
var symbolName = getSymbolOfNode(element).name;
|
||||
var propertyType = getTypeOfPropertyOfContextualType(type, symbolName);
|
||||
if (propertyType) {
|
||||
return propertyType;
|
||||
}
|
||||
}
|
||||
|
||||
var nameNode = element.name;
|
||||
var propertyHasNumericName = nameNode.kind === SyntaxKind.ComputedPropertyName
|
||||
? isNumericComputedName(<ComputedPropertyName>nameNode)
|
||||
: isNumericName((<Identifier>nameNode).text);
|
||||
return propertyHasNumericName && getIndexTypeOfContextualType(type, IndexKind.Number) ||
|
||||
getIndexTypeOfContextualType(type, IndexKind.String);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -5370,6 +5383,10 @@ module ts {
|
||||
return createArrayType(getUnionType(elementTypes));
|
||||
}
|
||||
|
||||
function isNumericComputedName(name: ComputedPropertyName): boolean {
|
||||
return !!(checkExpression(name.expression).flags & TypeFlags.Number);
|
||||
}
|
||||
|
||||
function isNumericName(name: string) {
|
||||
// The intent of numeric names is that
|
||||
// - they are names with text in a numeric form, and that
|
||||
@@ -5395,15 +5412,20 @@ module ts {
|
||||
return (+name).toString() === name;
|
||||
}
|
||||
|
||||
function checkComputedPropertyName(node: ComputedPropertyName): void {
|
||||
var computedNameType = checkExpression(node.expression);
|
||||
function checkComputedPropertyName(node: ComputedPropertyName): Type {
|
||||
var links = getNodeLinks(node.expression);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = checkExpression(node.expression);
|
||||
|
||||
// This will only allow types number, string, or any. Any types more complex will
|
||||
// be disallowed, even union types like string | number. In the future, we might consider
|
||||
// allowing types like that.
|
||||
if ((computedNameType.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.Any)) === 0) {
|
||||
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_or_any);
|
||||
// This will only allow types number, string, or any. Any types more complex will
|
||||
// be disallowed, even union types like string | number. In the future, we might consider
|
||||
// allowing types like that.
|
||||
if ((links.resolvedType.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.Any)) === 0) {
|
||||
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_or_any);
|
||||
}
|
||||
}
|
||||
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function checkObjectLiteral(node: ObjectLiteralExpression, contextualMapper?: TypeMapper): Type {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//// [computedPropertyNamesContextualType1.ts]
|
||||
interface I {
|
||||
[s: string]: (x: string) => number;
|
||||
[s: number]: (x: any) => number; // Doesn't get hit
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
["" + 0](y) { return y.length; },
|
||||
["" + 1]: y => y.length
|
||||
}
|
||||
|
||||
//// [computedPropertyNamesContextualType1.js]
|
||||
var o = {
|
||||
["" + 0](y) {
|
||||
return y.length;
|
||||
},
|
||||
["" + 1]: function (y) { return y.length; }
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
=== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType1.ts ===
|
||||
interface I {
|
||||
>I : I
|
||||
|
||||
[s: string]: (x: string) => number;
|
||||
>s : string
|
||||
>x : string
|
||||
|
||||
[s: number]: (x: any) => number; // Doesn't get hit
|
||||
>s : number
|
||||
>x : any
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
>o : I
|
||||
>I : I
|
||||
>{ ["" + 0](y) { return y.length; }, ["" + 1]: y => y.length} : { [x: string]: undefined; [x: number]: undefined; }
|
||||
|
||||
["" + 0](y) { return y.length; },
|
||||
>"" + 0 : string
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
|
||||
["" + 1]: y => y.length
|
||||
>"" + 1 : string
|
||||
>y => y.length : (y: string) => number
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//// [computedPropertyNamesContextualType2.ts]
|
||||
interface I {
|
||||
[s: string]: (x: any) => number; // Doesn't get hit
|
||||
[s: number]: (x: string) => number;
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
[+"foo"](y) { return y.length; },
|
||||
[+"bar"]: y => y.length
|
||||
}
|
||||
|
||||
//// [computedPropertyNamesContextualType2.js]
|
||||
var o = {
|
||||
[+"foo"](y) {
|
||||
return y.length;
|
||||
},
|
||||
[+"bar"]: function (y) { return y.length; }
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
=== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType2.ts ===
|
||||
interface I {
|
||||
>I : I
|
||||
|
||||
[s: string]: (x: any) => number; // Doesn't get hit
|
||||
>s : string
|
||||
>x : any
|
||||
|
||||
[s: number]: (x: string) => number;
|
||||
>s : number
|
||||
>x : string
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
>o : I
|
||||
>I : I
|
||||
>{ [+"foo"](y) { return y.length; }, [+"bar"]: y => y.length} : { [x: string]: undefined; [x: number]: undefined; }
|
||||
|
||||
[+"foo"](y) { return y.length; },
|
||||
>+"foo" : number
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
|
||||
[+"bar"]: y => y.length
|
||||
>+"bar" : number
|
||||
>y => y.length : (y: string) => number
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//// [computedPropertyNamesContextualType3.ts]
|
||||
interface I {
|
||||
[s: string]: (x: string) => number;
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
[+"foo"](y) { return y.length; },
|
||||
[+"bar"]: y => y.length
|
||||
}
|
||||
|
||||
//// [computedPropertyNamesContextualType3.js]
|
||||
var o = {
|
||||
[+"foo"](y) {
|
||||
return y.length;
|
||||
},
|
||||
[+"bar"]: function (y) { return y.length; }
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
=== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType3.ts ===
|
||||
interface I {
|
||||
>I : I
|
||||
|
||||
[s: string]: (x: string) => number;
|
||||
>s : string
|
||||
>x : string
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
>o : I
|
||||
>I : I
|
||||
>{ [+"foo"](y) { return y.length; }, [+"bar"]: y => y.length} : { [x: string]: undefined; }
|
||||
|
||||
[+"foo"](y) { return y.length; },
|
||||
>+"foo" : number
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
|
||||
[+"bar"]: y => y.length
|
||||
>+"bar" : number
|
||||
>y => y.length : (y: string) => number
|
||||
>y : string
|
||||
>y.length : number
|
||||
>y : string
|
||||
>length : number
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// @target: es6
|
||||
interface I {
|
||||
[s: string]: (x: string) => number;
|
||||
[s: number]: (x: any) => number; // Doesn't get hit
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
["" + 0](y) { return y.length; },
|
||||
["" + 1]: y => y.length
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// @target: es6
|
||||
interface I {
|
||||
[s: string]: (x: any) => number; // Doesn't get hit
|
||||
[s: number]: (x: string) => number;
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
[+"foo"](y) { return y.length; },
|
||||
[+"bar"]: y => y.length
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// @target: es6
|
||||
interface I {
|
||||
[s: string]: (x: string) => number;
|
||||
}
|
||||
|
||||
var o: I = {
|
||||
[+"foo"](y) { return y.length; },
|
||||
[+"bar"]: y => y.length
|
||||
}
|
||||
Reference in New Issue
Block a user