Skip to content

Commit 288c5f7

Browse files
committed
Partial Types (microsoft#11233)
1 parent 7b34b61 commit 288c5f7

24 files changed

+811
-14
lines changed

src/compiler/checker.ts

Lines changed: 135 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,10 @@ namespace ts {
15751575
}
15761576

15771577
function symbolIsValue(symbol: Symbol): boolean {
1578+
if (symbol.partialSource) {
1579+
return symbolIsValue(symbol.partialSource);
1580+
}
1581+
15781582
// If it is an instantiated symbol, then it is a value if the symbol it is an
15791583
// instantiation of is a value.
15801584
if (symbol.flags & SymbolFlags.Instantiated) {
@@ -1630,6 +1634,12 @@ namespace ts {
16301634
return type;
16311635
}
16321636

1637+
function createPartialTypeFromObjectType(target: Type): Type {
1638+
const type = <PartialType>createType(TypeFlags.Partial | TypeFlags.ObjectType);
1639+
type.type = target;
1640+
return type;
1641+
}
1642+
16331643
// A reserved member name starts with two underscores, but the third character cannot be an underscore
16341644
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
16351645
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
@@ -2217,6 +2227,10 @@ namespace ts {
22172227
}
22182228
writer.writeKeyword("this");
22192229
}
2230+
else if (type.flags & TypeFlags.Partial) {
2231+
writer.writeKeyword("partial ");
2232+
writeType((type as PartialType).type, flags);
2233+
}
22202234
else if (getObjectFlags(type) & ObjectFlags.Reference) {
22212235
writeTypeReference(<TypeReference>type, nextFlags);
22222236
}
@@ -2799,6 +2813,7 @@ namespace ts {
27992813
case SyntaxKind.UnionType:
28002814
case SyntaxKind.IntersectionType:
28012815
case SyntaxKind.ParenthesizedType:
2816+
case SyntaxKind.PartialType:
28022817
return isDeclarationVisible(<Declaration>node.parent);
28032818

28042819
// Default binding, import specifier and namespace import is visible
@@ -3542,7 +3557,24 @@ namespace ts {
35423557
return links.type;
35433558
}
35443559

3560+
function getTypeOfPartialPropertySymbol(symbol: Symbol): Type {
3561+
const links = getSymbolLinks(symbol);
3562+
if (!links.type) {
3563+
const type = getTypeOfSymbol(symbol.partialSource);
3564+
if (strictNullChecks) {
3565+
links.type = getUnionType([type, undefinedType]);
3566+
}
3567+
else {
3568+
links.type = type;
3569+
}
3570+
}
3571+
return links.type;
3572+
}
3573+
35453574
function getTypeOfSymbol(symbol: Symbol): Type {
3575+
if (symbol.partialSource) {
3576+
return getTypeOfPartialPropertySymbol(symbol);
3577+
}
35463578
if (symbol.flags & SymbolFlags.Instantiated) {
35473579
return getTypeOfInstantiatedSymbol(symbol);
35483580
}
@@ -4199,6 +4231,29 @@ namespace ts {
41994231
resolveObjectTypeMembers(type, source, typeParameters, typeArguments);
42004232
}
42014233

4234+
function resolvePartialTypeMembers(type: PartialType) {
4235+
const source = getPropertiesOfType(type.type);
4236+
const members = createMap<Symbol>();
4237+
for (const member of source) {
4238+
if (member.flags & SymbolFlags.Optional) {
4239+
members[member.name] = member;
4240+
}
4241+
else {
4242+
const synthetic = createSymbol(member.flags | SymbolFlags.SyntheticProperty | SymbolFlags.Optional, member.name);
4243+
getSymbolLinks(synthetic).originalType = type.type;
4244+
synthetic.partialSource = member;
4245+
members[member.name] = synthetic;
4246+
}
4247+
}
4248+
const stringIndex = getIndexInfoOfType(type.type, IndexKind.String);
4249+
const numberIndex = getIndexInfoOfType(type.type, IndexKind.Number);
4250+
const stringIndexType = stringIndex && (strictNullChecks ? getUnionType([stringIndex.type, undefinedType]) : stringIndex.type);
4251+
const numberIndexType = numberIndex && (strictNullChecks ? getUnionType([numberIndex.type, undefinedType]) : numberIndex.type);
4252+
setObjectTypeMembers(type, members, emptyArray, emptyArray,
4253+
stringIndex && createIndexInfo(stringIndexType, stringIndex.isReadonly, stringIndex.declaration),
4254+
numberIndex && createIndexInfo(numberIndexType, numberIndex.isReadonly, numberIndex.declaration));
4255+
}
4256+
42024257
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[],
42034258
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature {
42044259
const sig = new Signature(checker);
@@ -4420,6 +4475,8 @@ namespace ts {
44204475
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
44214476
resolveAnonymousTypeMembers(<AnonymousType>type);
44224477
}
4478+
else if ((<ObjectType>type).flags & TypeFlags.Partial) {
4479+
resolvePartialTypeMembers(<PartialType>type);
44234480
}
44244481
else if (type.flags & TypeFlags.Union) {
44254482
resolveUnionTypeMembers(<UnionType>type);
@@ -5695,6 +5752,7 @@ namespace ts {
56955752
return links.resolvedType;
56965753
}
56975754

5755+
<<<<<<< 7b34b612beda66b0812462a3feeabc63852cd842
56985756
function getIndexTypeForTypeParameter(type: TypeParameter) {
56995757
if (!type.resolvedIndexType) {
57005758
type.resolvedIndexType = <IndexType>createType(TypeFlags.Index);
@@ -5721,7 +5779,17 @@ namespace ts {
57215779
function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
57225780
const links = getNodeLinks(node);
57235781
if (!links.resolvedType) {
5724-
links.resolvedType = getIndexType(getTypeFromTypeNodeNoAlias(node.type));
5782+
switch (node.operator) {
5783+
case SyntaxKind.KeyOfKeyword:
5784+
links.resolvedType = getIndexType(getTypeFromTypeNodeNoAlias(node.type));
5785+
break;
5786+
case SyntaxKind.PartialKeyword:
5787+
links.resolvedType = getPartialType(getTypeOfNode(node.type));
5788+
break;
5789+
default:
5790+
Debug.fail('Unknown operator of type operator type')
5791+
break;
5792+
}
57255793
}
57265794
return links.resolvedType;
57275795
}
@@ -5834,6 +5902,32 @@ namespace ts {
58345902
return links.resolvedType;
58355903
}
58365904

5905+
function getPartialType(type: Type): Type {
5906+
if (type.resolvedPartialType) {
5907+
return type.resolvedPartialType;
5908+
}
5909+
5910+
if (type.flags & TypeFlags.Partial) {
5911+
// partial partial T === partial T
5912+
return type;
5913+
}
5914+
if (type.flags & TypeFlags.Union) {
5915+
return type.resolvedPartialType = getUnionType((type as UnionType).types.map(getPartialType));
5916+
}
5917+
if (type.flags & TypeFlags.Intersection) {
5918+
return type.resolvedPartialType = getIntersectionType((type as IntersectionType).types.map(getPartialType));
5919+
}
5920+
if (type.flags & TypeFlags.ObjectType) {
5921+
return type.resolvedPartialType = createPartialTypeFromObjectType(type);
5922+
}
5923+
5924+
// Type parameter
5925+
Debug.assert(!!(type.flags & TypeFlags.TypeParameter));
5926+
const result = <PartialType>createType(TypeFlags.Partial);
5927+
result.type = type;
5928+
return type.resolvedPartialType = result;
5929+
}
5930+
58375931
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
58385932
const links = getNodeLinks(node);
58395933
if (!links.resolvedType) {
@@ -5983,6 +6077,8 @@ namespace ts {
59836077
return getTypeFromUnionTypeNode(<UnionTypeNode>node, aliasSymbol, aliasTypeArguments);
59846078
case SyntaxKind.IntersectionType:
59856079
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node, aliasSymbol, aliasTypeArguments);
6080+
case SyntaxKind.PartialType:
6081+
return getTypeFromPartialTypeNode(<PartialTypeNode>node);
59866082
case SyntaxKind.ParenthesizedType:
59876083
case SyntaxKind.JSDocNullableType:
59886084
case SyntaxKind.JSDocNonNullableType:
@@ -6272,6 +6368,9 @@ namespace ts {
62726368
if (type.flags & TypeFlags.IndexedAccess) {
62736369
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
62746370
}
6371+
if (type.flags & TypeFlags.Partial) {
6372+
return getPartialType(instantiateType((<PartialType>type).type, mapper));
6373+
}
62756374
}
62766375
return type;
62776376
}
@@ -6755,6 +6854,17 @@ namespace ts {
67556854

67566855
if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
67576856

6857+
// If the target is a 'partial T', the only allowed source is T, partial T, or {}
6858+
if ((target.flags & (TypeFlags.Partial | TypeFlags.ObjectType)) === TypeFlags.Partial) {
6859+
if ((source === emptyObjectType) || ((<PartialType>target).type === source)) {
6860+
return Ternary.True;
6861+
}
6862+
if (reportErrors) {
6863+
reportRelationError(headMessage, source, target);
6864+
}
6865+
return Ternary.False;
6866+
}
6867+
67586868
if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
67596869
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
67606870
if (reportErrors) {
@@ -15072,6 +15182,10 @@ namespace ts {
1507215182
getTypeFromIndexedAccessTypeNode(node);
1507315183
}
1507415184

15185+
function checkPartialType(node: PartialTypeNode) {
15186+
checkSourceElement(node.type);
15187+
}
15188+
1507515189
function isPrivateWithinAmbient(node: Node): boolean {
1507615190
return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node);
1507715191
}
@@ -18307,6 +18421,8 @@ namespace ts {
1830718421
case SyntaxKind.UnionType:
1830818422
case SyntaxKind.IntersectionType:
1830918423
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
18424+
case SyntaxKind.PartialType:
18425+
return checkPartialType(<PartialTypeNode>node);
1831018426
case SyntaxKind.ParenthesizedType:
1831118427
case SyntaxKind.TypeOperator:
1831218428
return checkSourceElement((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
@@ -18662,7 +18778,7 @@ namespace ts {
1866218778
node = node.parent;
1866318779
}
1866418780

18665-
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
18781+
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference);
1866618782
}
1866718783

1866818784
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
@@ -19054,15 +19170,23 @@ namespace ts {
1905419170

1905519171
function getRootSymbols(symbol: Symbol): Symbol[] {
1905619172
if (symbol.flags & SymbolFlags.SyntheticProperty) {
19057-
const symbols: Symbol[] = [];
19058-
const name = symbol.name;
19059-
forEach(getSymbolLinks(symbol).containingType.types, t => {
19060-
const symbol = getPropertyOfType(t, name);
19061-
if (symbol) {
19062-
symbols.push(symbol);
19063-
}
19064-
});
19065-
return symbols;
19173+
const links = getSymbolLinks(symbol);
19174+
if (links.containingType) {
19175+
const symbols: Symbol[] = [];
19176+
const name = symbol.name;
19177+
19178+
forEach(links.containingType.types, t => {
19179+
const symbol = getPropertyOfType(t, name);
19180+
if (symbol) {
19181+
symbols.push(symbol);
19182+
}
19183+
});
19184+
return symbols;
19185+
}
19186+
else if (links.originalType) {
19187+
return [links.originalType.symbol];
19188+
}
19189+
return emptyArray;
1906619190
}
1906719191
else if (symbol.flags & SymbolFlags.Transient) {
1906819192
let target: Symbol;

src/compiler/parser.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ namespace ts {
133133
case SyntaxKind.UnionType:
134134
case SyntaxKind.IntersectionType:
135135
return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types);
136+
case SyntaxKind.PartialType:
137+
return visitNode(cbNode, (<PartialTypeNode>node).type);
136138
case SyntaxKind.ParenthesizedType:
137139
case SyntaxKind.TypeOperator:
138140
return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type);
@@ -426,10 +428,11 @@ namespace ts {
426428
case SyntaxKind.PartiallyEmittedExpression:
427429
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
428430
case SyntaxKind.JSDocLiteralType:
429-
return visitNode(cbNode, (<JSDocLiteralType>node).literal);
431+
return visitNode(cbNode, (<JSDocLiteralType>node).literal);
430432
}
431433
}
432434

435+
433436
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
434437
performance.mark("beforeParse");
435438
const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
@@ -2042,6 +2045,13 @@ namespace ts {
20422045
return finishNode(node);
20432046
}
20442047

2048+
function parsePartialType(): PartialTypeNode {
2049+
const node = <PartialTypeNode>createNode(SyntaxKind.PartialType);
2050+
parseExpected(SyntaxKind.PartialKeyword);
2051+
node.type = parseType();
2052+
return finishNode(node);
2053+
}
2054+
20452055
function parseTypeParameter(): TypeParameterDeclaration {
20462056
const node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
20472057
node.name = parseIdentifier();
@@ -2477,6 +2487,9 @@ namespace ts {
24772487
return parseTupleType();
24782488
case SyntaxKind.OpenParenToken:
24792489
return parseParenthesizedType();
2490+
case SyntaxKind.PartialKeyword:
2491+
const partialNode = tryParse(parsePartialType);
2492+
return partialNode || parseTypeReference();
24802493
default:
24812494
return parseTypeReference();
24822495
}

src/compiler/scanner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ namespace ts {
9999
"null": SyntaxKind.NullKeyword,
100100
"number": SyntaxKind.NumberKeyword,
101101
"package": SyntaxKind.PackageKeyword,
102+
"partial": SyntaxKind.PartialKeyword,
102103
"private": SyntaxKind.PrivateKeyword,
103104
"protected": SyntaxKind.ProtectedKeyword,
104105
"public": SyntaxKind.PublicKeyword,

src/compiler/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ namespace ts {
182182
UndefinedKeyword,
183183
FromKeyword,
184184
GlobalKeyword,
185-
OfKeyword, // LastKeyword and LastToken
185+
OfKeyword,
186+
PartialKeyword,
186187

187188
// Parse tree nodes
188189

@@ -215,6 +216,7 @@ namespace ts {
215216
TupleType,
216217
UnionType,
217218
IntersectionType,
219+
PartialType,
218220
ParenthesizedType,
219221
ThisType,
220222
TypeOperator,
@@ -902,6 +904,7 @@ namespace ts {
902904
literal: Expression;
903905
}
904906

907+
// @kind(SyntaxKind.StringLiteralType)
905908
export interface StringLiteral extends LiteralExpression {
906909
kind: SyntaxKind.StringLiteral;
907910
/* @internal */ textSourceNode?: Identifier | StringLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
@@ -2576,6 +2579,7 @@ namespace ts {
25762579
/* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere
25772580
/* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
25782581
/* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
2582+
/* @internal */ partialSource?: Symbol; // True if the symbol is a synthetic partial property
25792583
}
25802584

25812585
/* @internal */
@@ -2589,6 +2593,7 @@ namespace ts {
25892593
mapper?: TypeMapper; // Type mapper for instantiation alias
25902594
referenced?: boolean; // True if alias symbol has been referenced as a value
25912595
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
2596+
originalType?: Type; // Originating type for a partial type synthetic property
25922597
hasNonUniformType?: boolean; // True if constituents have non-uniform types
25932598
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
25942599
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
@@ -2688,6 +2693,7 @@ namespace ts {
26882693
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
26892694
/* @internal */
26902695
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
2696+
Partial = 1 << 24, // Partial type
26912697

26922698
/* @internal */
26932699
Nullable = Undefined | Null,
@@ -2728,6 +2734,7 @@ namespace ts {
27282734
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
27292735
aliasSymbol?: Symbol; // Alias associated with type
27302736
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
2737+
resolvedPartialType?: Type; // If we create a partial type for this, it's cached here
27312738
}
27322739

27332740
/* @internal */
@@ -2772,6 +2779,11 @@ namespace ts {
27722779
objectFlags: ObjectFlags;
27732780
}
27742781

2782+
// Partial types
2783+
export interface PartialType extends Type {
2784+
type: Type;
2785+
}
2786+
27752787
// Class and interface types (TypeFlags.Class and TypeFlags.Interface)
27762788
export interface InterfaceType extends ObjectType {
27772789
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)

src/services/symbolDisplay.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ namespace ts.SymbolDisplay {
5858
if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) {
5959
return ScriptElementKind.memberVariableElement;
6060
}
61-
Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method));
6261
});
6362
if (!unionPropertyKind) {
6463
// If this was union of all methods,

0 commit comments

Comments
 (0)