Skip to content

Spread type #13288

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

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 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
205 changes: 200 additions & 5 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 @@ -412,6 +412,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 @@ -1174,6 +1176,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 @@ -2607,6 +2610,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 @@ -2620,7 +2643,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
24 changes: 20 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ namespace ts {
GetKeyword,
IsKeyword,
KeyOfKeyword,
SpreadKeyword,
ModuleKeyword,
NamespaceKeyword,
NeverKeyword,
Expand Down Expand Up @@ -216,6 +217,7 @@ namespace ts {
TupleType,
UnionType,
IntersectionType,
SpreadType,
ParenthesizedType,
ThisType,
TypeOperator,
Expand Down Expand Up @@ -886,6 +888,12 @@ namespace ts {
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 @@ -2596,7 +2604,7 @@ namespace ts {
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 @@ -2788,6 +2796,7 @@ namespace ts {
/* @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 @@ -2805,13 +2814,13 @@ namespace ts {
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 @@ -2932,6 +2941,13 @@ namespace ts {

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 @@ -2955,7 +2971,7 @@ namespace ts {
}

/* @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.

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

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

// any results in any
Expand Down Expand Up @@ -79,6 +81,23 @@ let computedAfter: { a: number, b: string, "at the end": number } =
let a = 12;
let shortCutted: { a: number, b: string } = { ...o, a }

// 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 @@ -111,7 +130,9 @@ 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 and functions result in { }
var spreadNull = __assign({}, null);
var spreadUndefined = __assign({}, undefined);
var spreadFunc = __assign({}, (function () { }));
// any results in any
var anything;
Expand Down Expand Up @@ -148,4 +169,21 @@ var computedAfter = __assign({}, o, (_c = { b: 'yeah' }, _c['at the end'] = 14,
// shortcut syntax
var a = 12;
var shortCutted = __assign({}, o, { a: a });
// 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