Skip to content
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

Spread type #13288

Closed
wants to merge 11 commits into from
215 changes: 207 additions & 8 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ namespace ts {
return emitUnionType(<UnionTypeNode>type);
case SyntaxKind.IntersectionType:
return emitIntersectionType(<IntersectionTypeNode>type);
case SyntaxKind.SpreadType:
return emitSpreadType(type as SpreadTypeNode);
case SyntaxKind.ParenthesizedType:
return emitParenType(<ParenthesizedTypeNode>type);
case SyntaxKind.TypeOperator:
Expand Down Expand Up @@ -1175,6 +1177,15 @@ namespace ts {
writeLine();
}

function emitSpreadType(type: SpreadTypeNode) {
write("spread(");
emitType(type.left);
write(",")
emitType(type.right);
write(")");
writeLine();
}

function emitVariableDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) {
// If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted
// so there is no check needed to see if declaration is visible
Expand Down
25 changes: 24 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ namespace ts {
visitNode(cbNode, (<ShorthandPropertyAssignment>node).objectAssignmentInitializer);
case SyntaxKind.SpreadAssignment:
return visitNode(cbNode, (<SpreadAssignment>node).expression);
case SyntaxKind.SpreadType:
return visitNode(cbNode, (node as SpreadTypeNode).left) ||
visitNode(cbNode, (node as SpreadTypeNode).right);
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
Expand Down Expand Up @@ -2611,6 +2614,26 @@ namespace ts {
return type;
}

function parseSpreadType(): TypeNode {
const node = createNode(SyntaxKind.SpreadType) as SpreadTypeNode;
parseExpected(SyntaxKind.SpreadKeyword);
parseExpected(SyntaxKind.OpenParenToken);
node.left = parseType();
if (parseOptional(SyntaxKind.CommaToken)) {
node.right = parseType();
}
parseExpected(SyntaxKind.CloseParenToken);
return finishNode(node);
}

function parseSpreadTypeOrHigher() {
switch (token()) {
case SyntaxKind.SpreadKeyword:
return parseSpreadType();
}
return parseArrayTypeOrHigher();
}

function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword) {
const node = <TypeOperatorNode>createNode(SyntaxKind.TypeOperator);
parseExpected(operator);
Expand All @@ -2624,7 +2647,7 @@ namespace ts {
case SyntaxKind.KeyOfKeyword:
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
}
return parseArrayTypeOrHigher();
return parseSpreadTypeOrHigher();
}

function parseUnionOrIntersectionType(kind: SyntaxKind, parseConstituentType: () => TypeNode, operator: SyntaxKind): TypeNode {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ namespace ts {
"global": SyntaxKind.GlobalKeyword,
"return": SyntaxKind.ReturnKeyword,
"set": SyntaxKind.SetKeyword,
"spread": SyntaxKind.SpreadKeyword,
"static": SyntaxKind.StaticKeyword,
"string": SyntaxKind.StringKeyword,
"super": SyntaxKind.SuperKeyword,
Expand Down
30 changes: 20 additions & 10 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
GetKeyword,
IsKeyword,
KeyOfKeyword,
SpreadKeyword,
ModuleKeyword,
NamespaceKeyword,
NeverKeyword,
Expand Down Expand Up @@ -235,6 +236,7 @@
TupleType,
UnionType,
IntersectionType,
SpreadType,
ParenthesizedType,
ThisType,
TypeOperator,
Expand Down Expand Up @@ -905,6 +907,12 @@
kind: SyntaxKind.IntersectionType;
}

export interface SpreadTypeNode extends TypeNode {
kind: SyntaxKind.SpreadType;
left: TypeNode;
right?: TypeNode;
}

export interface ParenthesizedTypeNode extends TypeNode {
kind: SyntaxKind.ParenthesizedType;
type: TypeNode;
Expand Down Expand Up @@ -2491,12 +2499,6 @@
CannotBeNamed
}

/* @internal */
export const enum SyntheticSymbolKind {
UnionOrIntersection,
Spread
}

export const enum TypePredicateKind {
This,
Identifier
Expand Down Expand Up @@ -2622,7 +2624,7 @@
Merged = 0x02000000, // Merged symbol (created during program binding)
Transient = 0x04000000, // Transient symbol (created during type check)
Prototype = 0x08000000, // Prototype property (no source representation)
SyntheticProperty = 0x10000000, // Property in union or intersection type
SyntheticProperty = 0x10000000, // Property in union, intersection or spread type
Optional = 0x20000000, // Optional property
ExportStar = 0x40000000, // Export * declaration

Expand Down Expand Up @@ -2815,6 +2817,7 @@
/* @internal */
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
NonPrimitive = 1 << 24, // intrinsic object type
Spread = 1 << 25, // Spread type

/* @internal */
Nullable = Undefined | Null,
Expand All @@ -2832,13 +2835,13 @@
BooleanLike = Boolean | BooleanLiteral,
EnumLike = Enum | EnumLiteral,
UnionOrIntersection = Union | Intersection,
StructuredType = Object | Union | Intersection,
StructuredType = Object | Union | Intersection | Spread,
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
TypeVariable = TypeParameter | IndexedAccess,

// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive,
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | NonPrimitive | Spread,
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
Expand Down Expand Up @@ -2969,6 +2972,13 @@

export type StructuredType = ObjectType | UnionType | IntersectionType;

/* @internal */
export interface SpreadType extends Type {
left: SpreadType | ResolvedType;
// Note: probably should just make this to be Type now
right: TypeParameter | IntersectionType | IndexType | IndexedAccessType | ResolvedType;
}

/* @internal */
// An instantiated anonymous type has a target and a mapper
export interface AnonymousType extends ObjectType {
Expand All @@ -2992,7 +3002,7 @@
}

/* @internal */
// Resolved object, union, or intersection type
// Resolved object, spread, union, or intersection type
export interface ResolvedType extends ObjectType, UnionOrIntersectionType {
members: SymbolTable; // Properties by name
properties: Symbol[]; // Properties
Expand Down
6 changes: 3 additions & 3 deletions src/lib/es2015.core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ interface ObjectConstructor {
* @param target The target object to copy to.
* @param source The source object from which to copy properties.
*/
assign<T, U>(target: T, source: U): T & U;
assign<T, U>(target: T, source: U): spread(T, U);

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
Expand All @@ -287,7 +287,7 @@ interface ObjectConstructor {
* @param source1 The first source object from which to copy properties.
* @param source2 The second source object from which to copy properties.
*/
assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;
assign<T, U, V>(target: T, source1: U, source2: V): spread(spread(T, U), V);

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
Expand All @@ -297,7 +297,7 @@ interface ObjectConstructor {
* @param source2 The second source object from which to copy properties.
* @param source3 The third source object from which to copy properties.
*/
assign<T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
assign<T, U, V, W>(target: T, source1: U, source2: V, source3: W): spread(spread(spread(T, U), V), W);

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
Expand Down
21 changes: 0 additions & 21 deletions tests/baselines/reference/interfaceSpread.errors.txt

This file was deleted.

15 changes: 0 additions & 15 deletions tests/baselines/reference/interfaceSpread.js

This file was deleted.

49 changes: 43 additions & 6 deletions tests/baselines/reference/objectSpread.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ let getter: { a: number, c: number } =
{ ...op, c: 7 }
getter.a = 12;

// functions result in { }
// null, undefined, object and functions result in { }
let spreadNull = { ...null };
let spreadUndefined = { ...undefined };
let spreadNonPrimitive = { ...<object>{}};
let spreadFunc = { ...(function () { }) };

// any results in any
Expand Down Expand Up @@ -78,8 +81,24 @@ let computedAfter: { a: number, b: string, "at the end": number } =
// shortcut syntax
let a = 12;
let shortCutted: { a: number, b: string } = { ...o, a }
// non primitive
let spreadNonPrimitive = { ...<object>{}};

// generics
function f<T, U>(t: T, u: U): spread(spread(T, U), { id: string }) {
return { ...t, ...u, id: 'id' };
}

let exclusive: { id: string, a: number, b: string, c: string, d: boolean } =
f({ a: 1, b: 'yes' }, { c: 'no', d: false })
let overlap: { id: string, a: number, b: string } =
f({ a: 1 }, { a: 2, b: 'extra' })
let overlapConflict: { id:string, a: string } =
f({ a: 1 }, { a: 'mismatch' })
let overwriteId: { id: string, a: number, c: number, d: string } =
f({ a: 1, id: true }, { c: 1, d: 'no' })

class D { m() { }; q = 2; }
let classesAreWrong: spread(spread({ id: string }, C), D) =
f(new C(), new D())


//// [objectSpread.js]
Expand Down Expand Up @@ -112,7 +131,10 @@ var propertyNested = { a: __assign({}, o) };
var op = { get a() { return 6; } };
var getter = __assign({}, op, { c: 7 });
getter.a = 12;
// functions result in { }
// null, undefined, object and functions result in { }
var spreadNull = __assign({}, null);
var spreadUndefined = __assign({}, undefined);
var spreadNonPrimitive = __assign({}, {});
var spreadFunc = __assign({}, (function () { }));
// any results in any
var anything;
Expand Down Expand Up @@ -149,6 +171,21 @@ var computedAfter = __assign({}, o, (_c = { b: 'yeah' }, _c['at the end'] = 14,
// shortcut syntax
var a = 12;
var shortCutted = __assign({}, o, { a: a });
// non primitive
var spreadNonPrimitive = __assign({}, {});
// generics
function f(t, u) {
return __assign({}, t, u, { id: 'id' });
}
var exclusive = f({ a: 1, b: 'yes' }, { c: 'no', d: false });
var overlap = f({ a: 1 }, { a: 2, b: 'extra' });
var overlapConflict = f({ a: 1 }, { a: 'mismatch' });
var overwriteId = f({ a: 1, id: true }, { c: 1, d: 'no' });
var D = (function () {
function D() {
this.q = 2;
}
D.prototype.m = function () { };
;
return D;
}());
var classesAreWrong = f(new C(), new D());
var _a, _b, _c;
Loading