Skip to content

Commit

Permalink
Partial Types (microsoft#11233)
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanCavanaugh committed Nov 2, 2016
1 parent 137c99b commit e29ae15
Show file tree
Hide file tree
Showing 24 changed files with 812 additions and 13 deletions.
141 changes: 131 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,10 @@ namespace ts {
}

function symbolIsValue(symbol: Symbol): boolean {
if (symbol.partialSource) {
return symbolIsValue(symbol.partialSource);
}

// If it is an instantiated symbol, then it is a value if the symbol it is an
// instantiation of is a value.
if (symbol.flags & SymbolFlags.Instantiated) {
Expand Down Expand Up @@ -1629,6 +1633,12 @@ namespace ts {
return type;
}

function createPartialTypeFromObjectType(target: Type): Type {
const type = <PartialType>createType(TypeFlags.Partial | TypeFlags.ObjectType);
type.type = target;
return type;
}

// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
Expand Down Expand Up @@ -2216,6 +2226,10 @@ namespace ts {
}
writer.writeKeyword("this");
}
else if (type.flags & TypeFlags.Partial) {
writer.writeKeyword("partial ");
writeType((type as PartialType).type, flags);
}
else if (getObjectFlags(type) & ObjectFlags.Reference) {
writeTypeReference(<TypeReference>type, nextFlags);
}
Expand Down Expand Up @@ -2787,6 +2801,7 @@ namespace ts {
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.ParenthesizedType:
case SyntaxKind.PartialType:
return isDeclarationVisible(<Declaration>node.parent);

// Default binding, import specifier and namespace import is visible
Expand Down Expand Up @@ -3530,7 +3545,24 @@ namespace ts {
return links.type;
}

function getTypeOfPartialPropertySymbol(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const type = getTypeOfSymbol(symbol.partialSource);
if (strictNullChecks) {
links.type = getUnionType([type, undefinedType]);
}
else {
links.type = type;
}
}
return links.type;
}

function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.partialSource) {
return getTypeOfPartialPropertySymbol(symbol);
}
if (symbol.flags & SymbolFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
Expand Down Expand Up @@ -4187,6 +4219,29 @@ namespace ts {
resolveObjectTypeMembers(type, source, typeParameters, typeArguments);
}

function resolvePartialTypeMembers(type: PartialType) {
const source = getPropertiesOfType(type.type);
const members = createMap<Symbol>();
for (const member of source) {
if (member.flags & SymbolFlags.Optional) {
members[member.name] = member;
}
else {
const synthetic = createSymbol(member.flags | SymbolFlags.SyntheticProperty | SymbolFlags.Optional, member.name);
getSymbolLinks(synthetic).originalType = type.type;
synthetic.partialSource = member;
members[member.name] = synthetic;
}
}
const stringIndex = getIndexInfoOfType(type.type, IndexKind.String);
const numberIndex = getIndexInfoOfType(type.type, IndexKind.Number);
const stringIndexType = stringIndex && (strictNullChecks ? getUnionType([stringIndex.type, undefinedType]) : stringIndex.type);
const numberIndexType = numberIndex && (strictNullChecks ? getUnionType([numberIndex.type, undefinedType]) : numberIndex.type);
setObjectTypeMembers(type, members, emptyArray, emptyArray,
stringIndex && createIndexInfo(stringIndexType, stringIndex.isReadonly, stringIndex.declaration),
numberIndex && createIndexInfo(numberIndexType, numberIndex.isReadonly, numberIndex.declaration));
}

function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[],
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature {
const sig = new Signature(checker);
Expand Down Expand Up @@ -4407,6 +4462,8 @@ namespace ts {
}
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
resolveAnonymousTypeMembers(<AnonymousType>type);
} else if ((<ObjectType>type).objectFlags & ObjectFlags.Partial) {
resolvePartialTypeMembers(<PartialType>type);
}
}
else if (type.flags & TypeFlags.Union) {
Expand Down Expand Up @@ -5692,6 +5749,40 @@ namespace ts {
return links.resolvedType;
}

function getTypeFromPartialTypeNode(node: PartialTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getPartialType(getTypeOfNode(node.type));
}
return links.resolvedType;
}

function getPartialType(type: Type): Type {
if (type.resolvedPartialType) {
return type.resolvedPartialType;
}

if (type.flags & TypeFlags.Partial) {
// partial partial T === partial T
return type;
}
if (type.flags & TypeFlags.Union) {
return type.resolvedPartialType = getUnionType((type as UnionType).types.map(getPartialType));
}
if (type.flags & TypeFlags.Intersection) {
return type.resolvedPartialType = getIntersectionType((type as IntersectionType).types.map(getPartialType));
}
if (type.flags & TypeFlags.ObjectType) {
return type.resolvedPartialType = createPartialTypeFromObjectType(type);
}

// Type parameter
Debug.assert(!!(type.flags & TypeFlags.TypeParameter));
const result = <PartialType>createType(TypeFlags.Partial);
result.type = type;
return type.resolvedPartialType = result;
}

function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
Expand Down Expand Up @@ -5841,6 +5932,8 @@ namespace ts {
return getTypeFromUnionTypeNode(<UnionTypeNode>node, aliasSymbol, aliasTypeArguments);
case SyntaxKind.IntersectionType:
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node, aliasSymbol, aliasTypeArguments);
case SyntaxKind.PartialType:
return getTypeFromPartialTypeNode(<PartialTypeNode>node);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.JSDocNullableType:
case SyntaxKind.JSDocNonNullableType:
Expand Down Expand Up @@ -6120,6 +6213,9 @@ namespace ts {
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes);
}
if (type.flags & TypeFlags.Partial) {
return getPartialType(instantiateType((<PartialType>type).type, mapper));
}
}
return type;
}
Expand Down Expand Up @@ -6603,6 +6699,17 @@ namespace ts {

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

// If the target is a 'partial T', the only allowed source is T, partial T, or {}
if ((target.flags & (TypeFlags.Partial | TypeFlags.ObjectType)) === TypeFlags.Partial) {
if ((source === emptyObjectType) || ((<PartialType>target).type === source)) {
return Ternary.True;
}
if (reportErrors) {
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}

if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
if (reportErrors) {
Expand Down Expand Up @@ -15021,6 +15128,10 @@ namespace ts {
forEach(node.types, checkSourceElement);
}

function checkPartialType(node: PartialTypeNode) {
checkSourceElement(node.type);
}

function isPrivateWithinAmbient(node: Node): boolean {
return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node);
}
Expand Down Expand Up @@ -18287,6 +18398,8 @@ namespace ts {
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
case SyntaxKind.PartialType:
return checkPartialType(<PartialTypeNode>node);
case SyntaxKind.ParenthesizedType:
return checkSourceElement((<ParenthesizedTypeNode>node).type);
case SyntaxKind.FunctionDeclaration:
Expand Down Expand Up @@ -18639,7 +18752,7 @@ namespace ts {
node = node.parent;
}

return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference);
}

function isHeritageClauseElementIdentifier(entityName: Node): boolean {
Expand Down Expand Up @@ -19031,15 +19144,23 @@ namespace ts {

function getRootSymbols(symbol: Symbol): Symbol[] {
if (symbol.flags & SymbolFlags.SyntheticProperty) {
const symbols: Symbol[] = [];
const name = symbol.name;
forEach(getSymbolLinks(symbol).containingType.types, t => {
const symbol = getPropertyOfType(t, name);
if (symbol) {
symbols.push(symbol);
}
});
return symbols;
const links = getSymbolLinks(symbol);
if (links.containingType) {
const symbols: Symbol[] = [];
const name = symbol.name;

forEach(links.containingType.types, t => {
const symbol = getPropertyOfType(t, name);
if (symbol) {
symbols.push(symbol);
}
});
return symbols;
}
else if (links.originalType) {
return [links.originalType.symbol];
}
return emptyArray;
}
else if (symbol.flags & SymbolFlags.Transient) {
let target: Symbol;
Expand Down
15 changes: 14 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ namespace ts {
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types);
case SyntaxKind.PartialType:
return visitNode(cbNode, (<PartialTypeNode>node).type);
case SyntaxKind.ParenthesizedType:
return visitNode(cbNode, (<ParenthesizedTypeNode>node).type);
case SyntaxKind.LiteralType:
Expand Down Expand Up @@ -422,10 +424,11 @@ namespace ts {
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
case SyntaxKind.JSDocLiteralType:
return visitNode(cbNode, (<JSDocLiteralType>node).literal);
return visitNode(cbNode, (<JSDocLiteralType>node).literal);
}
}


export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
performance.mark("beforeParse");
const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
Expand Down Expand Up @@ -2038,6 +2041,13 @@ namespace ts {
return finishNode(node);
}

function parsePartialType(): PartialTypeNode {
const node = <PartialTypeNode>createNode(SyntaxKind.PartialType);
parseExpected(SyntaxKind.PartialKeyword);
node.type = parseType();
return finishNode(node);
}

function parseTypeParameter(): TypeParameterDeclaration {
const node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
node.name = parseIdentifier();
Expand Down Expand Up @@ -2473,6 +2483,9 @@ namespace ts {
return parseTupleType();
case SyntaxKind.OpenParenToken:
return parseParenthesizedType();
case SyntaxKind.PartialKeyword:
const partialNode = tryParse(parsePartialType);
return partialNode || parseTypeReference();
default:
return parseTypeReference();
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ namespace ts {
"null": SyntaxKind.NullKeyword,
"number": SyntaxKind.NumberKeyword,
"package": SyntaxKind.PackageKeyword,
"partial": SyntaxKind.PartialKeyword,
"private": SyntaxKind.PrivateKeyword,
"protected": SyntaxKind.ProtectedKeyword,
"public": SyntaxKind.PublicKeyword,
Expand Down
19 changes: 18 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ namespace ts {
UndefinedKeyword,
FromKeyword,
GlobalKeyword,
OfKeyword, // LastKeyword and LastToken
OfKeyword,
PartialKeyword,

// Parse tree nodes

Expand Down Expand Up @@ -214,6 +215,7 @@ namespace ts {
TupleType,
UnionType,
IntersectionType,
PartialType,
ParenthesizedType,
ThisType,
LiteralType,
Expand Down Expand Up @@ -882,6 +884,12 @@ namespace ts {
type: TypeNode;
}

// @kind(SyntaxKind.PartialType)
export interface PartialTypeNode extends TypeNode {
type: TypeNode;
}

// @kind(SyntaxKind.StringLiteralType)
export interface LiteralTypeNode extends TypeNode {
kind: SyntaxKind.LiteralType;
literal: Expression;
Expand Down Expand Up @@ -2565,6 +2573,7 @@ namespace ts {
/* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere
/* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
/* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
/* @internal */ partialSource?: Symbol; // True if the symbol is a synthetic partial property
}

/* @internal */
Expand All @@ -2578,6 +2587,7 @@ namespace ts {
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
originalType?: Type; // Originating type for a partial type synthetic property
hasNonUniformType?: boolean; // True if constituents have non-uniform types
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
Expand Down Expand Up @@ -2675,6 +2685,7 @@ namespace ts {
ContainsObjectLiteral = 1 << 20, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 21, // Type is or contains object literal type
Partial = 1 << 22, // Partial type

/* @internal */
Nullable = Undefined | Null,
Expand Down Expand Up @@ -2715,6 +2726,7 @@ namespace ts {
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
aliasSymbol?: Symbol; // Alias associated with type
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
resolvedPartialType?: Type; // If we create a partial type for this, it's cached here
}

/* @internal */
Expand Down Expand Up @@ -2759,6 +2771,11 @@ namespace ts {
objectFlags: ObjectFlags;
}

// Partial types
export interface PartialType extends Type {
type: Type;
}

// Class and interface types (TypeFlags.Class and TypeFlags.Interface)
export interface InterfaceType extends ObjectType {
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
Expand Down
1 change: 0 additions & 1 deletion src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ namespace ts.SymbolDisplay {
if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) {
return ScriptElementKind.memberVariableElement;
}
Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method));
});
if (!unionPropertyKind) {
// If this was union of all methods,
Expand Down
Loading

0 comments on commit e29ae15

Please sign in to comment.