Skip to content

Set symbol on union that is returned from getSpreadType #18965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7846,7 +7846,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;
}
Expand All @@ -7857,10 +7857,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;
Expand Down Expand Up @@ -7918,7 +7918,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) {
Expand Down Expand Up @@ -13858,7 +13864,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;
Expand All @@ -13870,7 +13876,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;
}
Expand Down Expand Up @@ -13915,14 +13921,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;
}
Expand Down Expand Up @@ -14043,7 +14042,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();
}
Expand All @@ -14052,7 +14051,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;
Expand All @@ -14063,7 +14062,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);
}
Expand Down
19 changes: 19 additions & 0 deletions tests/baselines/reference/objectSpreadIndexSignature.errors.txt
Original file line number Diff line number Diff line change
@@ -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 };

28 changes: 12 additions & 16 deletions tests/baselines/reference/objectSpreadIndexSignature.js
Original file line number Diff line number Diff line change
@@ -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];
Expand All @@ -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);
63 changes: 30 additions & 33 deletions tests/baselines/reference/objectSpreadIndexSignature.symbols
Original file line number Diff line number Diff line change
@@ -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)

52 changes: 26 additions & 26 deletions tests/baselines/reference/objectSpreadIndexSignature.types
Original file line number Diff line number Diff line change
@@ -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;
>c : boolean
}
let indexed: Indexed;
>indexed : Indexed
>Indexed : Indexed

let indexed2: Indexed2;
>indexed2 : Indexed2
>Indexed2 : Indexed2
declare let indexed3: { [n: string]: number };
>indexed3 : { [n: string]: number; }
>n : string

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

Expand All @@ -38,15 +26,27 @@ 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];
>ii[1001] : number | boolean
>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

21 changes: 9 additions & 12 deletions tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts
Original file line number Diff line number Diff line change
@@ -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 };