mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 22:32:33 -05:00
Switch spread types to a binary representation.
This allows much easier creation of spread types. Spread type creation is now much smarter, and creates object types when possible. When it's not, it produces a spread type that simplifies its input into a standard structure for containing type parameters. The spread type is no longer related to union or intersection types. Spread types containing union and intersection types are not yet correctly handled.
This commit is contained in:
@@ -2310,33 +2310,40 @@ namespace ts {
|
||||
writePunctuation(writer, SyntaxKind.OpenBraceToken);
|
||||
writer.writeLine();
|
||||
writer.increaseIndent();
|
||||
let printFollowingPunctuation = false;
|
||||
for (const t of type.types) {
|
||||
if (printFollowingPunctuation) {
|
||||
|
||||
writeSpreadTypeWorker(type, /*atEnd*/true, type.symbol);
|
||||
|
||||
writer.decreaseIndent();
|
||||
writePunctuation(writer, SyntaxKind.CloseBraceToken);
|
||||
}
|
||||
|
||||
function writeSpreadTypeWorker(type: SpreadType, atEnd: boolean, container: Symbol): void {
|
||||
if (type.left.flags & TypeFlags.Spread) {
|
||||
writeSpreadTypeWorker(type.left as SpreadType, /*atEnd*/false, container);
|
||||
}
|
||||
else {
|
||||
const saveInObjectTypeLiteral = inObjectTypeLiteral;
|
||||
inObjectTypeLiteral = true;
|
||||
writeObjectLiteralType(resolveStructuredTypeMembers(type.left));
|
||||
inObjectTypeLiteral = saveInObjectTypeLiteral;
|
||||
}
|
||||
if (type.right.symbol === container) {
|
||||
const saveInObjectTypeLiteral = inObjectTypeLiteral;
|
||||
inObjectTypeLiteral = true;
|
||||
writeObjectLiteralType(resolveStructuredTypeMembers(type.right));
|
||||
inObjectTypeLiteral = saveInObjectTypeLiteral;
|
||||
}
|
||||
else {
|
||||
writePunctuation(writer, SyntaxKind.DotDotDotToken);
|
||||
writeType(type.right, TypeFormatFlags.None);
|
||||
if (atEnd) {
|
||||
writeSpace(writer);
|
||||
}
|
||||
else {
|
||||
writePunctuation(writer, SyntaxKind.SemicolonToken);
|
||||
writer.writeLine();
|
||||
}
|
||||
if (t.isDeclaredProperty) {
|
||||
const saveInObjectTypeLiteral = inObjectTypeLiteral;
|
||||
inObjectTypeLiteral = true;
|
||||
writeObjectLiteralType(resolveStructuredTypeMembers(t));
|
||||
printFollowingPunctuation = false;
|
||||
inObjectTypeLiteral = saveInObjectTypeLiteral;
|
||||
}
|
||||
else {
|
||||
writePunctuation(writer, SyntaxKind.DotDotDotToken);
|
||||
writeType(t, TypeFormatFlags.None);
|
||||
printFollowingPunctuation = true;
|
||||
}
|
||||
}
|
||||
const resolved = resolveStructuredTypeMembers(type);
|
||||
writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword);
|
||||
writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword);
|
||||
writer.decreaseIndent();
|
||||
if (printFollowingPunctuation) {
|
||||
writeSpace(writer);
|
||||
}
|
||||
writePunctuation(writer, SyntaxKind.CloseBraceToken);
|
||||
}
|
||||
|
||||
function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) {
|
||||
@@ -3522,6 +3529,9 @@ namespace ts {
|
||||
if (symbol.flags & SymbolFlags.Instantiated) {
|
||||
return getTypeOfInstantiatedSymbol(symbol);
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.SyntheticProperty && symbol.syntheticKind === SyntheticSymbolKind.Spread) {
|
||||
return getTypeOfSpreadProperty(symbol);
|
||||
}
|
||||
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
|
||||
return getTypeOfVariableOrParameterOrProperty(symbol);
|
||||
}
|
||||
@@ -3540,6 +3550,15 @@ namespace ts {
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
function getTypeOfSpreadProperty(symbol: Symbol) {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
links.type = getUnionType([getTypeOfSymbol(links.leftSpread), getTypeOfSymbol(links.rightSpread)]);
|
||||
}
|
||||
return links.type;
|
||||
|
||||
}
|
||||
|
||||
function getTargetType(type: ObjectType): Type {
|
||||
return type.flags & TypeFlags.Reference ? (<TypeReference>type).target : type;
|
||||
}
|
||||
@@ -4340,21 +4359,6 @@ namespace ts {
|
||||
setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
|
||||
}
|
||||
|
||||
function resolveSpreadTypeMembers(type: SpreadType) {
|
||||
// The members and properties collections are empty for spread types. To get all properties of an
|
||||
// spread type use getPropertiesOfType.
|
||||
let stringIndexInfo: IndexInfo = getIndexInfoOfSymbol(type.symbol, IndexKind.String);
|
||||
let numberIndexInfo: IndexInfo = getIndexInfoOfSymbol(type.symbol, IndexKind.Number);
|
||||
for (let i = type.types.length - 1; i > -1; i--) {
|
||||
const t = type.types[i];
|
||||
if (!t.isDeclaredProperty) {
|
||||
stringIndexInfo = unionIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String));
|
||||
numberIndexInfo = unionIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number));
|
||||
}
|
||||
}
|
||||
setObjectTypeMembers(type, emptySymbols, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
|
||||
}
|
||||
|
||||
function resolveAnonymousTypeMembers(type: AnonymousType) {
|
||||
const symbol = type.symbol;
|
||||
if (type.target) {
|
||||
@@ -4421,9 +4425,6 @@ namespace ts {
|
||||
else if (type.flags & TypeFlags.Intersection) {
|
||||
resolveIntersectionTypeMembers(<IntersectionType>type);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Spread) {
|
||||
resolveSpreadTypeMembers(<SpreadType>type);
|
||||
}
|
||||
}
|
||||
return <ResolvedType>type;
|
||||
}
|
||||
@@ -4433,9 +4434,6 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.ObjectType) {
|
||||
return resolveStructuredTypeMembers(<ObjectType>type).properties;
|
||||
}
|
||||
if (type.flags & TypeFlags.Spread) {
|
||||
return getPropertiesOfType(type);
|
||||
}
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
@@ -4451,11 +4449,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertiesOfUnionOrIntersectionOrSpreadType(type: TypeOperatorType): Symbol[] {
|
||||
const getProperty = type.flags & TypeFlags.Spread ? getPropertyOfSpreadType : getUnionOrIntersectionProperty;
|
||||
function getPropertiesOfUnionOrIntersectionType(type: TypeOperatorType): Symbol[] {
|
||||
for (const current of type.types) {
|
||||
for (const prop of getPropertiesOfType(current)) {
|
||||
getProperty(type, prop.name);
|
||||
getUnionOrIntersectionProperty(type, prop.name);
|
||||
}
|
||||
// The properties of a union type are those that are present in all constituent types, so
|
||||
// we only need to check the properties of the first type
|
||||
@@ -4480,8 +4477,10 @@ namespace ts {
|
||||
|
||||
function getPropertiesOfType(type: Type): Symbol[] {
|
||||
type = getApparentType(type);
|
||||
return type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.Spread) ? getPropertiesOfUnionOrIntersectionOrSpreadType(<UnionType>type) :
|
||||
getPropertiesOfObjectType(type);
|
||||
if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
return getPropertiesOfUnionOrIntersectionType(<UnionType>type);
|
||||
}
|
||||
return getPropertiesOfObjectType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4499,6 +4498,10 @@ namespace ts {
|
||||
return type.resolvedApparentType;
|
||||
}
|
||||
|
||||
function getApparentTypeOfSpread(type: SpreadType) {
|
||||
return getSpreadType([getApparentType(type.left), getApparentType(type.right)], type.symbol, undefined, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a type parameter, return the base constraint of the type parameter. For the string, number,
|
||||
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
|
||||
@@ -4507,7 +4510,10 @@ namespace ts {
|
||||
function getApparentType(type: Type): Type {
|
||||
if (type.flags & TypeFlags.TypeParameter) {
|
||||
type = getApparentTypeOfTypeParameter(<TypeParameter>type);
|
||||
}
|
||||
}
|
||||
if (type.flags & TypeFlags.Spread) {
|
||||
type = getApparentTypeOfSpread(type as SpreadType);
|
||||
}
|
||||
if (type.flags & TypeFlags.StringLike) {
|
||||
type = globalStringType;
|
||||
}
|
||||
@@ -4585,6 +4591,7 @@ namespace ts {
|
||||
propTypes.push(type);
|
||||
}
|
||||
const result = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | flags, name);
|
||||
result.syntheticKind === SyntheticSymbolKind.UnionOrIntersection;
|
||||
result.containingType = containingType;
|
||||
result.hasNonUniformType = hasNonUniformType;
|
||||
result.isPartial = isPartial;
|
||||
@@ -4614,61 +4621,6 @@ namespace ts {
|
||||
return property;
|
||||
}
|
||||
|
||||
function createSpreadProperty(containingType: SpreadType, name: string): Symbol {
|
||||
const types = containingType.types;
|
||||
return createUnionOrIntersectionOrSpreadPropertySymbol(containingType, name, () => {
|
||||
let props: Symbol[];
|
||||
// Result is readonly if any source is readonly
|
||||
let isReadonly = false;
|
||||
// Result is optional if all sources are optional
|
||||
let commonFlags = SymbolFlags.Optional;
|
||||
for (let i = types.length - 1; i > -1; i--) {
|
||||
const type = getApparentType(types[i]);
|
||||
if (type !== unknownType) {
|
||||
const prop = getPropertyOfType(type, name);
|
||||
if (prop) {
|
||||
if (prop.flags & SymbolFlags.Method && !types[i].isDeclaredProperty ||
|
||||
prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor)) {
|
||||
// skip non-object-literal methods and set-only properties and keep looking
|
||||
continue;
|
||||
}
|
||||
if (!props) {
|
||||
props = [prop];
|
||||
}
|
||||
else if (!contains(props, prop)) {
|
||||
props.unshift(prop);
|
||||
}
|
||||
if (isReadonlySymbol(prop)) {
|
||||
isReadonly = true;
|
||||
}
|
||||
if (getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected)) {
|
||||
// return immediately because even if prop is optional, it would make the unioned spread property private
|
||||
return [undefined, false, false, 0];
|
||||
}
|
||||
if (!(prop.flags & SymbolFlags.Optional)) {
|
||||
// Reset extraFlags to None since we found a non-optional property
|
||||
commonFlags = SymbolFlags.None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [props, isReadonly, /*isPartial*/ false, commonFlags];
|
||||
});
|
||||
}
|
||||
|
||||
function getPropertyOfSpreadType(type: TypeOperatorType, name: string): Symbol {
|
||||
const properties = type.resolvedProperties || (type.resolvedProperties = createMap<Symbol>());
|
||||
let property = properties[name];
|
||||
if (!property) {
|
||||
property = createSpreadProperty(type as SpreadType, name);
|
||||
if (property) {
|
||||
properties[name] = property;
|
||||
}
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
function getPropertyOfUnionOrIntersectionType(type: TypeOperatorType, name: string): Symbol {
|
||||
const property = getUnionOrIntersectionProperty(type, name);
|
||||
// We need to filter out partial properties in union types
|
||||
@@ -4702,9 +4654,6 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
return getPropertyOfUnionOrIntersectionType(<TypeOperatorType>type, name);
|
||||
}
|
||||
if (type.flags & TypeFlags.Spread) {
|
||||
return getPropertyOfSpreadType(<SpreadType>type, name);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -5775,16 +5724,14 @@ namespace ts {
|
||||
let type: ObjectType;
|
||||
if (isSpread) {
|
||||
let members: Map<Symbol>;
|
||||
const spreads: SpreadElementType[] = [];
|
||||
const spreads: Type[] = [];
|
||||
for (const member of (node as TypeLiteralNode).members) {
|
||||
if (member.kind === SyntaxKind.SpreadTypeElement) {
|
||||
if (members) {
|
||||
const t = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined) as SpreadElementType;
|
||||
t.isDeclaredProperty = true;
|
||||
spreads.push(t);
|
||||
spreads.push(createAnonymousType(node.symbol, members, emptyArray, emptyArray, undefined, undefined));
|
||||
members = undefined;
|
||||
}
|
||||
spreads.push(getTypeFromTypeNode((member as SpreadTypeElement).type) as SpreadElementType);
|
||||
spreads.push(getTypeFromTypeNode((member as SpreadTypeElement).type));
|
||||
}
|
||||
else if (member.kind !== SyntaxKind.CallSignature &&
|
||||
member.kind !== SyntaxKind.ConstructSignature &&
|
||||
@@ -5803,11 +5750,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (members) {
|
||||
const t = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined) as SpreadElementType;
|
||||
t.isDeclaredProperty = true;
|
||||
spreads.push(t);
|
||||
spreads.push(createAnonymousType(node.symbol, members, emptyArray, emptyArray, undefined, undefined));
|
||||
}
|
||||
return getSpreadType(spreads, node.symbol);
|
||||
return getSpreadType(spreads, node.symbol, aliasSymbol, aliasTypeArguments);
|
||||
}
|
||||
else {
|
||||
type = createObjectType(TypeFlags.Anonymous, node.symbol);
|
||||
@@ -5819,13 +5764,94 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getSpreadType(types: SpreadElementType[], symbol: Symbol, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
|
||||
function getSpreadType(types: Type[], symbol: Symbol, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
|
||||
if (types.length === 0) {
|
||||
return emptyObjectType;
|
||||
}
|
||||
const id = getTypeListId(types);
|
||||
if (id in spreadTypes) {
|
||||
return spreadTypes[id];
|
||||
}
|
||||
const right = types.pop();
|
||||
if (right.flags & TypeFlags.Spread) {
|
||||
// spread is right associative and associativity applies, so transform
|
||||
// (T ... U) ... V to T ... (U ... V)
|
||||
const rspread = right as SpreadType;
|
||||
if (rspread.left !== emptyObjectType) {
|
||||
types.push(rspread.left);
|
||||
}
|
||||
types.push(rspread.right);
|
||||
return getSpreadType(types, symbol, aliasSymbol, aliasTypeArguments);
|
||||
}
|
||||
const atBeginning = types.length === 0;
|
||||
const left = getSpreadType(types, symbol, aliasSymbol, aliasTypeArguments);
|
||||
if (right.flags & (TypeFlags.Null | TypeFlags.Undefined)) {
|
||||
return left;
|
||||
}
|
||||
if (right.flags & TypeFlags.TypeParameter &&
|
||||
left.flags & TypeFlags.Spread &&
|
||||
(left as SpreadType).right.flags & TypeFlags.TypeParameter &&
|
||||
right.symbol === (left as SpreadType).right.symbol) {
|
||||
// for types like T ... T, just return ... T
|
||||
return left;
|
||||
}
|
||||
if (!(right.flags & (TypeFlags.Union | TypeFlags.Intersection | TypeFlags.TypeParameter | TypeFlags.Spread))
|
||||
&& !(left.flags & (TypeFlags.Union | TypeFlags.Intersection | TypeFlags.TypeParameter | TypeFlags.Spread))) {
|
||||
const members = createMap<Symbol | undefined>();
|
||||
const skippedPrivateMembers = createMap<boolean>();
|
||||
let stringIndexInfo = unionIndexInfos(getIndexInfoOfType(left, IndexKind.String), getIndexInfoOfType(right, IndexKind.String));
|
||||
let numberIndexInfo = unionIndexInfos(getIndexInfoOfType(left, IndexKind.Number), getIndexInfoOfType(right, IndexKind.Number));
|
||||
if (atBeginning) {
|
||||
// only get index info from the entire type once per spread type
|
||||
stringIndexInfo = unionIndexInfos(stringIndexInfo, getIndexInfoOfSymbol(symbol, IndexKind.String));
|
||||
numberIndexInfo = unionIndexInfos(numberIndexInfo, getIndexInfoOfSymbol(symbol, IndexKind.Number));
|
||||
}
|
||||
const isFromSpread = right.symbol !== symbol;
|
||||
for (const rightProp of getPropertiesOfType(right)) {
|
||||
if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) {
|
||||
skippedPrivateMembers[rightProp.name] = true;
|
||||
}
|
||||
else if (!(rightProp.flags & SymbolFlags.Method && isFromSpread) &&
|
||||
!(rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor))) {
|
||||
// skip methods from spreads and accessors with setters but no getters
|
||||
members[rightProp.name] = rightProp;
|
||||
}
|
||||
}
|
||||
for (const leftProp of getPropertiesOfType(left)) {
|
||||
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
|
||||
|| leftProp.name in skippedPrivateMembers) {
|
||||
// skip set-only properties (methods have already been skipped by the recursive call to getSpreadType)
|
||||
continue;
|
||||
}
|
||||
if (leftProp.name in members) {
|
||||
const rightProp = members[leftProp.name];
|
||||
if (rightProp.flags & SymbolFlags.Optional) {
|
||||
const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations);
|
||||
const flags = SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | (leftProp.flags & SymbolFlags.Optional);
|
||||
const result = <TransientSymbol>createSymbol(flags, leftProp.name);
|
||||
result.syntheticKind = SyntheticSymbolKind.Spread;
|
||||
result.leftSpread = leftProp;
|
||||
result.rightSpread = rightProp;
|
||||
result.declarations = declarations;
|
||||
if (declarations.length) {
|
||||
result.valueDeclaration = declarations[0];
|
||||
}
|
||||
result.isReadonly = isReadonlySymbol(rightProp) || isReadonlySymbol(leftProp);
|
||||
members[leftProp.name] = result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
members[leftProp.name] = leftProp;
|
||||
}
|
||||
}
|
||||
return createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
|
||||
}
|
||||
// one side is a type parameter (TODO: Or union or intersection)
|
||||
const spread = spreadTypes[id] = createObjectType(TypeFlags.Spread, symbol) as SpreadType;
|
||||
spread.types = filter(types, t => !(t.flags & (TypeFlags.Null | TypeFlags.Undefined)));
|
||||
Debug.assert(!!(left.flags & (TypeFlags.Spread | TypeFlags.ObjectType)));
|
||||
Debug.assert(!!(right.flags & (TypeFlags.TypeParameter | TypeFlags.ObjectType)));
|
||||
spread.left = left as SpreadType | ResolvedType;
|
||||
spread.right = right as TypeParameter | ResolvedType;
|
||||
spread.aliasSymbol = aliasSymbol;
|
||||
spread.aliasTypeArguments = aliasTypeArguments;
|
||||
return spread;
|
||||
@@ -6238,7 +6264,8 @@ namespace ts {
|
||||
return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes);
|
||||
}
|
||||
if (type.flags & TypeFlags.Spread) {
|
||||
return getSpreadType(instantiateList((type as SpreadType).types, mapper, instantiateType) as SpreadElementType[], type.symbol, type.aliasSymbol, mapper.targetTypes);
|
||||
const spread = type as SpreadType;
|
||||
return getSpreadType([instantiateType(spread.left, mapper), instantiateType(spread.right, mapper)], type.symbol, type.aliasSymbol, mapper.targetTypes);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
@@ -6776,38 +6803,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (source.flags & TypeFlags.Spread) {
|
||||
if (target.flags & TypeFlags.TypeParameter) {
|
||||
let hasTypeParameter = false;
|
||||
let typeParametersAreEqual = true;
|
||||
for (const t of (source as SpreadType).types) {
|
||||
if (t.flags & TypeFlags.TypeParameter) {
|
||||
hasTypeParameter = true;
|
||||
if (t !== target) {
|
||||
typeParametersAreEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasTypeParameter && typeParametersAreEqual) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.Spread) {
|
||||
const sourceParameters = filter((source as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter));
|
||||
const targetParameters = filter((target as SpreadType).types, t => !!(t.flags & TypeFlags.TypeParameter));
|
||||
if (sourceParameters.length !== targetParameters.length) {
|
||||
// you only see this for spreads with type parameters (TODO: and unions/intersections)
|
||||
if (target.flags & TypeFlags.Spread) {
|
||||
if (!(spreadTypeRelatedTo(source as SpreadType, target as SpreadType))) {
|
||||
reportRelationError(headMessage, source, target);
|
||||
return Ternary.False;
|
||||
}
|
||||
for (let i = 0; i < sourceParameters.length; i++) {
|
||||
if (sourceParameters[i].symbol !== targetParameters[i].symbol) {
|
||||
reportRelationError(headMessage, source, target);
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
|
||||
if (result = objectTypeRelatedTo(source, source, target, reportStructuralErrors)) {
|
||||
const apparentSource = getApparentType(source);
|
||||
if (result = objectTypeRelatedTo(apparentSource, source, getApparentType(target), reportStructuralErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
}
|
||||
@@ -6815,6 +6819,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (source.flags & TypeFlags.TypeParameter) {
|
||||
if (target.flags & TypeFlags.Spread) {
|
||||
// T is assignable to ...T
|
||||
if (source.symbol === (target as SpreadType).right.symbol
|
||||
&& (target as SpreadType).left === emptyObjectType) {
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
|
||||
|
||||
if (!constraint || constraint.flags & TypeFlags.Any) {
|
||||
@@ -6844,7 +6855,7 @@ namespace ts {
|
||||
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
|
||||
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
|
||||
// relates to X. Thus, we include intersection types on the source side here.
|
||||
if (apparentSource.flags & (TypeFlags.ObjectType | TypeFlags.Intersection | TypeFlags.Spread) && target.flags & TypeFlags.ObjectType) {
|
||||
if (apparentSource.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
|
||||
// Report structural errors only if we haven't reported any errors yet
|
||||
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive);
|
||||
if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) {
|
||||
@@ -6866,6 +6877,43 @@ namespace ts {
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
function spreadTypeRelatedTo(source: SpreadType, target: SpreadType): boolean {
|
||||
// (Spread ... Object) | (Spread | Object ... TypeParameter)
|
||||
// in other words, if the right side is Object, then the left side must be a Spread.
|
||||
if (source.right.flags & TypeFlags.ObjectType &&
|
||||
target.right.flags & TypeFlags.ObjectType) {
|
||||
return spreadTypeRelatedTo(source.left as SpreadType, target.left as SpreadType);
|
||||
}
|
||||
if (source.right.flags & TypeFlags.ObjectType) {
|
||||
/// target.right is TypeParameter, skip source.right, but keep looking at target
|
||||
return spreadTypeRelatedTo(source.left as SpreadType, target);
|
||||
}
|
||||
if (target.right.flags & TypeFlags.ObjectType) {
|
||||
/// source.right is TypeParameter, skip target.right, but keep looking at source
|
||||
return spreadTypeRelatedTo(source, target.left as SpreadType);
|
||||
}
|
||||
else {
|
||||
// both rights are type parameters, so they must be identical
|
||||
// and both lefts must be the same:
|
||||
// if one left is object and the other is spread, that means the second has another type parameter. which isn't allowed
|
||||
if (target.right.symbol !== source.right.symbol) {
|
||||
return false;
|
||||
}
|
||||
if (source.left.flags & TypeFlags.Spread && target.left.flags & TypeFlags.Spread) {
|
||||
return spreadTypeRelatedTo(source.left as SpreadType, target.left as SpreadType);
|
||||
}
|
||||
else if (source.left.flags & TypeFlags.ObjectType && target.left.flags & TypeFlags.ObjectType) {
|
||||
return true; // let structural compatibility figure it out later
|
||||
}
|
||||
else {
|
||||
// one side is a spread, so it must have more type parameters, which will not be matched by the other side
|
||||
// return false immediately instead of descending to find this out.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isIdenticalTo(source: Type, target: Type): Ternary {
|
||||
let result: Ternary;
|
||||
if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
|
||||
@@ -10558,7 +10606,7 @@ namespace ts {
|
||||
|
||||
let propertiesTable = createMap<Symbol>();
|
||||
let propertiesArray: Symbol[] = [];
|
||||
const spreads: SpreadElementType[] = [];
|
||||
const spreads: Type[] = [];
|
||||
const contextualType = getApparentTypeOfContextualType(node);
|
||||
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
|
||||
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
|
||||
@@ -10631,15 +10679,13 @@ namespace ts {
|
||||
}
|
||||
else if (memberDecl.kind === SyntaxKind.SpreadElementExpression) {
|
||||
if (propertiesArray.length > 0) {
|
||||
const t = createObjectLiteralType() as SpreadElementType;
|
||||
t.isDeclaredProperty = true;
|
||||
spreads.push(t);
|
||||
spreads.push(createObjectLiteralType());
|
||||
propertiesArray = [];
|
||||
propertiesTable = createMap<Symbol>();
|
||||
hasComputedStringProperty = false;
|
||||
hasComputedNumberProperty = false;
|
||||
}
|
||||
spreads.push(checkExpression((memberDecl as SpreadElementExpression).expression) as SpreadElementType);
|
||||
spreads.push(checkExpression((memberDecl as SpreadElementExpression).expression));
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
@@ -10683,17 +10729,16 @@ namespace ts {
|
||||
|
||||
if (spreads.length > 0) {
|
||||
if (propertiesArray.length > 0) {
|
||||
const t = createObjectLiteralType() as SpreadElementType;
|
||||
t.isDeclaredProperty = true;
|
||||
spreads.push(t);
|
||||
spreads.push(createObjectLiteralType());
|
||||
}
|
||||
const propagatedFlags = getPropagatingFlagsOfTypes(spreads, /*excludeKinds*/ TypeFlags.Nullable);
|
||||
const spread = getSpreadType(spreads, node.symbol);
|
||||
const spread = getSpreadType(spreads, node.symbol, undefined, undefined);
|
||||
spread.flags |= propagatedFlags;
|
||||
return spread;
|
||||
}
|
||||
|
||||
return createObjectLiteralType();
|
||||
|
||||
function createObjectLiteralType() {
|
||||
const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.String) : undefined;
|
||||
const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined;
|
||||
@@ -10708,7 +10753,6 @@ namespace ts {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
|
||||
@@ -18943,15 +18987,22 @@ 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;
|
||||
if (symbol.syntheticKind === SyntheticSymbolKind.Spread) {
|
||||
const name = symbol.name;
|
||||
const links = getSymbolLinks(symbol);
|
||||
return [links.leftSpread, links.rightSpread];
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.Transient) {
|
||||
let target: Symbol;
|
||||
|
||||
@@ -2318,6 +2318,12 @@ namespace ts {
|
||||
NotAccessible,
|
||||
CannotBeNamed
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum SyntheticSymbolKind {
|
||||
UnionOrIntersection,
|
||||
Spread
|
||||
}
|
||||
|
||||
export const enum TypePredicateKind {
|
||||
This,
|
||||
@@ -2442,7 +2448,7 @@ namespace ts {
|
||||
Instantiated = 0x01000000, // Instantiated symbol
|
||||
Merged = 0x02000000, // Merged symbol (created during program binding)
|
||||
Transient = 0x04000000, // Transient symbol (created during type check)
|
||||
Prototype = 0x08000000, // Prototype property (no source representation)
|
||||
Prototype = 0x08000000, // Prototype property (no source representation)
|
||||
SyntheticProperty = 0x10000000, // Property in union, intersection or spread type
|
||||
Optional = 0x20000000, // Optional property
|
||||
ExportStar = 0x40000000, // Export * declaration
|
||||
@@ -2517,6 +2523,7 @@ namespace ts {
|
||||
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
|
||||
/* @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 */ syntheticKind?: SyntheticSymbolKind; // Synthetic symbols are either spread or union/intersection
|
||||
}
|
||||
|
||||
@@ -2530,6 +2537,8 @@ namespace ts {
|
||||
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
|
||||
mapper?: TypeMapper; // Type mapper for instantiation alias
|
||||
referenced?: boolean; // True if alias symbol has been referenced as a value
|
||||
containingType?: TypeOperatorType; // Containing union or intersection type for synthetic property
|
||||
leftSpread?: Symbol; // Left source for synthetic spread property
|
||||
rightSpread?: Symbol; // Right source for synthetic spread 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
|
||||
@@ -2759,13 +2768,9 @@ namespace ts {
|
||||
|
||||
export interface IntersectionType extends TypeOperatorType { }
|
||||
|
||||
/* @internal */
|
||||
export interface SpreadType extends TypeOperatorType {
|
||||
types: SpreadElementType[]; // Constituent types
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface SpreadElementType extends ResolvedType {
|
||||
/* @internal */
|
||||
export interface SpreadType extends Type {
|
||||
left: SpreadType | ResolvedType;
|
||||
right: TypeParameter | ResolvedType;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user