mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 20:37:00 -05:00
Allow object intersection types as class/interface base types
This commit is contained in:
@@ -3732,11 +3732,16 @@ namespace ts {
|
||||
return getObjectFlags(type) & ObjectFlags.Reference ? (<TypeReference>type).target : type;
|
||||
}
|
||||
|
||||
function hasBaseType(type: InterfaceType, checkBase: InterfaceType) {
|
||||
function hasBaseType(type: BaseType, checkBase: BaseType) {
|
||||
return check(type);
|
||||
function check(type: InterfaceType): boolean {
|
||||
const target = <InterfaceType>getTargetType(type);
|
||||
return target === checkBase || forEach(getBaseTypes(target), check);
|
||||
function check(type: BaseType): boolean {
|
||||
if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
|
||||
const target = <InterfaceType>getTargetType(type);
|
||||
return target === checkBase || forEach(getBaseTypes(target), check);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Intersection) {
|
||||
return forEach((<IntersectionType>type).types, check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3862,7 +3867,7 @@ namespace ts {
|
||||
return type.resolvedBaseConstructorType;
|
||||
}
|
||||
|
||||
function getBaseTypes(type: InterfaceType): ObjectType[] {
|
||||
function getBaseTypes(type: InterfaceType): BaseType[] {
|
||||
if (!type.resolvedBaseTypes) {
|
||||
if (type.objectFlags & ObjectFlags.Tuple) {
|
||||
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))];
|
||||
@@ -3922,11 +3927,11 @@ namespace ts {
|
||||
if (baseType === unknownType) {
|
||||
return;
|
||||
}
|
||||
if (!(getObjectFlags(getTargetType(baseType)) & ObjectFlags.ClassOrInterface)) {
|
||||
if (!isValidBaseType(baseType)) {
|
||||
error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType));
|
||||
return;
|
||||
}
|
||||
if (type === baseType || hasBaseType(<InterfaceType>baseType, type)) {
|
||||
if (type === baseType || hasBaseType(<BaseType>baseType, type)) {
|
||||
error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type,
|
||||
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
|
||||
return;
|
||||
@@ -3951,6 +3956,13 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
// A valid base type is any non-generic object type or intersection of non-generic
|
||||
// object types.
|
||||
function isValidBaseType(type: Type): boolean {
|
||||
return type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
|
||||
type.flags & TypeFlags.Intersection && !forEach((<IntersectionType>type).types, t => !isValidBaseType(t));
|
||||
}
|
||||
|
||||
function resolveBaseTypesOfInterface(type: InterfaceType): void {
|
||||
type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray;
|
||||
for (const declaration of type.symbol.declarations) {
|
||||
@@ -3958,8 +3970,8 @@ namespace ts {
|
||||
for (const node of getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
|
||||
const baseType = getTypeFromTypeNode(node);
|
||||
if (baseType !== unknownType) {
|
||||
if (getObjectFlags(getTargetType(baseType)) & ObjectFlags.ClassOrInterface) {
|
||||
if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) {
|
||||
if (isValidBaseType(baseType)) {
|
||||
if (type !== baseType && !hasBaseType(<BaseType>baseType, type)) {
|
||||
if (type.resolvedBaseTypes === emptyArray) {
|
||||
type.resolvedBaseTypes = [<ObjectType>baseType];
|
||||
}
|
||||
@@ -4317,6 +4329,9 @@ namespace ts {
|
||||
return createTypeReference((<TypeReference>type).target,
|
||||
concatenate((<TypeReference>type).typeArguments, [thisArgument || (<TypeReference>type).target.thisType]));
|
||||
}
|
||||
if (type.flags & TypeFlags.Intersection) {
|
||||
return getIntersectionType(map((<IntersectionType>type).types, t => getTypeWithThisArgument(t, thisArgument)));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -4350,8 +4365,8 @@ namespace ts {
|
||||
}
|
||||
const thisArgument = lastOrUndefined(typeArguments);
|
||||
for (const baseType of baseTypes) {
|
||||
const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(<ObjectType>instantiateType(baseType, mapper), thisArgument) : baseType;
|
||||
addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType));
|
||||
const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType;
|
||||
addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType));
|
||||
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
|
||||
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
|
||||
stringIndexInfo = stringIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.String);
|
||||
@@ -18216,16 +18231,18 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
const propDeclaration = prop.valueDeclaration;
|
||||
|
||||
// index is numeric and property name is not valid numeric literal
|
||||
if (indexKind === IndexKind.Number && !isNumericName(prop.valueDeclaration.name)) {
|
||||
if (indexKind === IndexKind.Number && propDeclaration && !isNumericName(propDeclaration.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// perform property check if property or indexer is declared in 'type'
|
||||
// this allows to rule out cases when both property and indexer are inherited from the base class
|
||||
let errorNode: Node;
|
||||
if (prop.valueDeclaration.name.kind === SyntaxKind.ComputedPropertyName || prop.parent === containingType.symbol) {
|
||||
errorNode = prop.valueDeclaration;
|
||||
if (propDeclaration && propDeclaration.name.kind === SyntaxKind.ComputedPropertyName || prop.parent === containingType.symbol) {
|
||||
errorNode = propDeclaration;
|
||||
}
|
||||
else if (indexDeclaration) {
|
||||
errorNode = indexDeclaration;
|
||||
@@ -18364,7 +18381,7 @@ namespace ts {
|
||||
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
|
||||
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
|
||||
|
||||
if (baseType.symbol.valueDeclaration &&
|
||||
if (baseType.symbol && baseType.symbol.valueDeclaration &&
|
||||
!isInAmbientContext(baseType.symbol.valueDeclaration) &&
|
||||
baseType.symbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration) {
|
||||
if (!isBlockScopedNameDeclaredBeforeUse(baseType.symbol.valueDeclaration, node)) {
|
||||
@@ -18437,7 +18454,7 @@ namespace ts {
|
||||
return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined);
|
||||
}
|
||||
|
||||
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: ObjectType): void {
|
||||
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): void {
|
||||
|
||||
// TypeScript 1.0 spec (April 2014): 8.2.3
|
||||
// A derived class inherits all members from its base class it doesn't override.
|
||||
@@ -18454,7 +18471,7 @@ namespace ts {
|
||||
// derived class instance member variables and accessors, but not by other kinds of members.
|
||||
|
||||
// NOTE: assignability is checked in checkClassDeclaration
|
||||
const baseProperties = getPropertiesOfObjectType(baseType);
|
||||
const baseProperties = getPropertiesOfType(baseType);
|
||||
for (const baseProperty of baseProperties) {
|
||||
const base = getTargetSymbol(baseProperty);
|
||||
|
||||
@@ -18578,7 +18595,7 @@ namespace ts {
|
||||
let ok = true;
|
||||
|
||||
for (const base of baseTypes) {
|
||||
const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType));
|
||||
const properties = getPropertiesOfType(getTypeWithThisArgument(base, type.thisType));
|
||||
for (const prop of properties) {
|
||||
const existing = seen.get(prop.name);
|
||||
if (!existing) {
|
||||
|
||||
@@ -2358,7 +2358,7 @@
|
||||
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo;
|
||||
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getBaseTypes(type: InterfaceType): ObjectType[];
|
||||
getBaseTypes(type: InterfaceType): BaseType[];
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
/**
|
||||
* Gets the type of a parameter at a given position in a signature.
|
||||
@@ -2910,9 +2910,12 @@
|
||||
/* @internal */
|
||||
resolvedBaseConstructorType?: Type; // Resolved base constructor type of class
|
||||
/* @internal */
|
||||
resolvedBaseTypes: ObjectType[]; // Resolved base types
|
||||
resolvedBaseTypes: BaseType[]; // Resolved base types
|
||||
}
|
||||
|
||||
// Object type or intersection of object types
|
||||
export type BaseType = ObjectType | IntersectionType;
|
||||
|
||||
export interface InterfaceTypeWithDeclaredMembers extends InterfaceType {
|
||||
declaredProperties: Symbol[]; // Declared members
|
||||
declaredCallSignatures: Signature[]; // Declared call signatures
|
||||
|
||||
@@ -379,7 +379,7 @@ namespace ts {
|
||||
getNumberIndexType(): Type {
|
||||
return this.checker.getIndexTypeOfType(this, IndexKind.Number);
|
||||
}
|
||||
getBaseTypes(): ObjectType[] {
|
||||
getBaseTypes(): BaseType[] {
|
||||
return this.flags & TypeFlags.Object && this.objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)
|
||||
? this.checker.getBaseTypes(<InterfaceType><Type>this)
|
||||
: undefined;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace ts {
|
||||
getConstructSignatures(): Signature[];
|
||||
getStringIndexType(): Type;
|
||||
getNumberIndexType(): Type;
|
||||
getBaseTypes(): ObjectType[];
|
||||
getBaseTypes(): BaseType[];
|
||||
getNonNullableType(): Type;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user