Merge pull request #18965 from Microsoft/set-symbol-on-union-of-spreads

Set symbol on union that is returned from `getSpreadType`
This commit is contained in:
Nathan Shively-Sanders 2017-10-11 13:25:45 -07:00 committed by GitHub
commit 461e29bbd8
6 changed files with 114 additions and 106 deletions

View File

@ -7878,7 +7878,7 @@ namespace ts {
* this function should be called in a left folding style, with left = previous result of getSpreadType
* and right = the new element to be spread.
*/
function getSpreadType(left: Type, right: Type): Type {
function getSpreadType(left: Type, right: Type, symbol: Symbol, propagatedFlags: TypeFlags): Type {
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
return anyType;
}
@ -7889,10 +7889,10 @@ namespace ts {
return left;
}
if (left.flags & TypeFlags.Union) {
return mapType(left, t => getSpreadType(t, right));
return mapType(left, t => getSpreadType(t, right, symbol, propagatedFlags));
}
if (right.flags & TypeFlags.Union) {
return mapType(right, t => getSpreadType(left, t));
return mapType(right, t => getSpreadType(left, t, symbol, propagatedFlags));
}
if (right.flags & TypeFlags.NonPrimitive) {
return nonPrimitiveType;
@ -7950,7 +7950,13 @@ namespace ts {
members.set(leftProp.escapedName, getNonReadonlySymbol(leftProp));
}
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = symbol;
return spread;
}
function getNonReadonlySymbol(prop: Symbol) {
@ -13914,7 +13920,7 @@ namespace ts {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
propertiesArray = [];
propertiesTable = createSymbolTable();
hasComputedStringProperty = false;
@ -13926,7 +13932,7 @@ namespace ts {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return unknownType;
}
spread = getSpreadType(spread, type);
spread = getSpreadType(spread, type, node.symbol, propagatedFlags);
offset = i + 1;
continue;
}
@ -13971,14 +13977,7 @@ namespace ts {
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
}
if (spread.flags & TypeFlags.Object) {
// only set the symbol and flags if this is a (fresh) object type
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = node.symbol;
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
}
return spread;
}
@ -14099,7 +14098,7 @@ namespace ts {
else {
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
attributesArray = [];
attributesTable = createSymbolTable();
}
@ -14108,7 +14107,7 @@ namespace ts {
hasSpreadAnyType = true;
}
if (isValidSpreadType(exprType)) {
spread = getSpreadType(spread, exprType);
spread = getSpreadType(spread, exprType, openingLikeElement.symbol, /*propagatedFlags*/ 0);
}
else {
typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType;
@ -14119,7 +14118,7 @@ namespace ts {
if (!hasSpreadAnyType) {
if (spread !== emptyObjectType) {
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
}
attributesArray = getPropertiesOfType(spread);
}

View File

@ -0,0 +1,19 @@
tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts(6,1): error TS7017: Element implicitly has an 'any' type because type '{ b: number; a: number; }' has no index signature.
==== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts (1 errors) ====
declare let indexed1: { [n: string]: number; a: number; };
declare let indexed2: { [n: string]: boolean; c: boolean; };
declare let indexed3: { [n: string]: number };
let i = { ...indexed1, b: 11 };
// only indexed has indexer, so i[101]: any
i[101];
~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{ b: number; a: number; }' has no index signature.
let ii = { ...indexed1, ...indexed2 };
// both have indexer, so i[1001]: number | boolean
ii[1001];
declare const b: boolean;
indexed3 = { ...b ? indexed3 : undefined };

View File

@ -1,23 +1,20 @@
//// [objectSpreadIndexSignature.ts]
interface Indexed {
[n: string]: number;
a: number;
}
interface Indexed2 {
[n: string]: boolean;
c: boolean;
}
let indexed: Indexed;
let indexed2: Indexed2;
let i = { ...indexed, b: 11 };
declare let indexed1: { [n: string]: number; a: number; };
declare let indexed2: { [n: string]: boolean; c: boolean; };
declare let indexed3: { [n: string]: number };
let i = { ...indexed1, b: 11 };
// only indexed has indexer, so i[101]: any
i[101];
let ii = { ...indexed, ...indexed2 };
let ii = { ...indexed1, ...indexed2 };
// both have indexer, so i[1001]: number | boolean
ii[1001];
declare const b: boolean;
indexed3 = { ...b ? indexed3 : undefined };
//// [objectSpreadIndexSignature.js]
"use strict";
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
@ -26,11 +23,10 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
}
return t;
};
var indexed;
var indexed2;
var i = __assign({}, indexed, { b: 11 });
var i = __assign({}, indexed1, { b: 11 });
// only indexed has indexer, so i[101]: any
i[101];
var ii = __assign({}, indexed, indexed2);
var ii = __assign({}, indexed1, indexed2);
// both have indexer, so i[1001]: number | boolean
ii[1001];
indexed3 = __assign({}, b ? indexed3 : undefined);

View File

@ -1,45 +1,42 @@
=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts ===
interface Indexed {
>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 0, 0))
declare let indexed1: { [n: string]: number; a: number; };
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 0, 25))
>a : Symbol(a, Decl(objectSpreadIndexSignature.ts, 0, 44))
[n: string]: number;
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 1, 5))
declare let indexed2: { [n: string]: boolean; c: boolean; };
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 1, 11))
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 1, 25))
>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 1, 45))
a: number;
>a : Symbol(Indexed.a, Decl(objectSpreadIndexSignature.ts, 1, 24))
}
interface Indexed2 {
>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 3, 1))
declare let indexed3: { [n: string]: number };
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 2, 25))
[n: string]: boolean;
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 5, 5))
c: boolean;
>c : Symbol(Indexed2.c, Decl(objectSpreadIndexSignature.ts, 5, 25))
}
let indexed: Indexed;
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 0, 0))
let indexed2: Indexed2;
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 9, 3))
>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 3, 1))
let i = { ...indexed, b: 11 };
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 10, 3))
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 21))
let i = { ...indexed1, b: 11 };
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 3, 22))
// only indexed has indexer, so i[101]: any
i[101];
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 10, 3))
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
let ii = { ...indexed, ...indexed2 };
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 13, 3))
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 9, 3))
let ii = { ...indexed1, ...indexed2 };
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 1, 11))
// both have indexer, so i[1001]: number | boolean
ii[1001];
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 13, 3))
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
declare const b: boolean;
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
indexed3 = { ...b ? indexed3 : undefined };
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
>undefined : Symbol(undefined)

View File

@ -1,34 +1,22 @@
=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts ===
interface Indexed {
>Indexed : Indexed
[n: string]: number;
declare let indexed1: { [n: string]: number; a: number; };
>indexed1 : { [n: string]: number; a: number; }
>n : string
a: number;
>a : number
}
interface Indexed2 {
>Indexed2 : Indexed2
[n: string]: boolean;
declare let indexed2: { [n: string]: boolean; c: boolean; };
>indexed2 : { [n: string]: boolean; c: boolean; }
>n : string
>c : boolean
declare let indexed3: { [n: string]: number };
>indexed3 : { [n: string]: number; }
>n : string
c: boolean;
>c : boolean
}
let indexed: Indexed;
>indexed : Indexed
>Indexed : Indexed
let indexed2: Indexed2;
>indexed2 : Indexed2
>Indexed2 : Indexed2
let i = { ...indexed, b: 11 };
let i = { ...indexed1, b: 11 };
>i : { b: number; a: number; }
>{ ...indexed, b: 11 } : { b: number; a: number; }
>indexed : Indexed
>{ ...indexed1, b: 11 } : { b: number; a: number; }
>indexed1 : { [n: string]: number; a: number; }
>b : number
>11 : 11
@ -38,11 +26,11 @@ i[101];
>i : { b: number; a: number; }
>101 : 101
let ii = { ...indexed, ...indexed2 };
let ii = { ...indexed1, ...indexed2 };
>ii : { [x: string]: number | boolean; c: boolean; a: number; }
>{ ...indexed, ...indexed2 } : { [x: string]: number | boolean; c: boolean; a: number; }
>indexed : Indexed
>indexed2 : Indexed2
>{ ...indexed1, ...indexed2 } : { [x: string]: number | boolean; c: boolean; a: number; }
>indexed1 : { [n: string]: number; a: number; }
>indexed2 : { [n: string]: boolean; c: boolean; }
// both have indexer, so i[1001]: number | boolean
ii[1001];
@ -50,3 +38,15 @@ ii[1001];
>ii : { [x: string]: number | boolean; c: boolean; a: number; }
>1001 : 1001
declare const b: boolean;
>b : boolean
indexed3 = { ...b ? indexed3 : undefined };
>indexed3 = { ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
>indexed3 : { [n: string]: number; }
>{ ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
>b ? indexed3 : undefined : { [n: string]: number; } | undefined
>b : boolean
>indexed3 : { [n: string]: number; }
>undefined : undefined

View File

@ -1,16 +1,13 @@
interface Indexed {
[n: string]: number;
a: number;
}
interface Indexed2 {
[n: string]: boolean;
c: boolean;
}
let indexed: Indexed;
let indexed2: Indexed2;
let i = { ...indexed, b: 11 };
// @strict: true
declare let indexed1: { [n: string]: number; a: number; };
declare let indexed2: { [n: string]: boolean; c: boolean; };
declare let indexed3: { [n: string]: number };
let i = { ...indexed1, b: 11 };
// only indexed has indexer, so i[101]: any
i[101];
let ii = { ...indexed, ...indexed2 };
let ii = { ...indexed1, ...indexed2 };
// both have indexer, so i[1001]: number | boolean
ii[1001];
declare const b: boolean;
indexed3 = { ...b ? indexed3 : undefined };