Merge pull request #13990 from Microsoft/fixPrivateProtected

Properly handle private and protected properties in intersections
This commit is contained in:
Anders Hejlsberg 2017-02-13 08:13:38 -10:00 committed by GitHub
commit 7cd0e1a0e6
11 changed files with 1204 additions and 153 deletions

View file

@ -60,9 +60,9 @@ namespace ts {
const emitResolver = createResolver();
const undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined");
undefinedSymbol.declarations = [];
const argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments");
const checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
@ -131,8 +131,8 @@ namespace ts {
const indexedAccessTypes = createMap<IndexedAccessType>();
const evolvingArrayTypes: EvolvingArrayType[] = [];
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown");
const resolvingSymbol = createSymbol(0, "__resolving__");
const anyType = createIntrinsicType(TypeFlags.Any, "any");
const autoType = createIntrinsicType(TypeFlags.Any, "any");
@ -154,7 +154,7 @@ namespace ts {
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral | SymbolFlags.Transient, "__type");
const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
emptyTypeLiteralSymbol.members = createMap<Symbol>();
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, undefined, undefined);
@ -415,9 +415,11 @@ namespace ts {
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string): Symbol {
function createSymbol(flags: SymbolFlags, name: string) {
symbolCount++;
return new Symbol(flags, name);
const symbol = <TransientSymbol>(new Symbol(flags | SymbolFlags.Transient, name));
symbol.checkFlags = 0;
return symbol;
}
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
@ -450,7 +452,7 @@ namespace ts {
}
function cloneSymbol(symbol: Symbol): Symbol {
const result = createSymbol(symbol.flags | SymbolFlags.Merged, symbol.name);
const result = createSymbol(symbol.flags, symbol.name);
result.declarations = symbol.declarations.slice(0);
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
@ -507,7 +509,7 @@ namespace ts {
target.set(id, sourceSymbol);
}
else {
if (!(targetSymbol.flags & SymbolFlags.Merged)) {
if (!(targetSymbol.flags & SymbolFlags.Transient)) {
targetSymbol = cloneSymbol(targetSymbol);
target.set(id, targetSymbol);
}
@ -544,7 +546,7 @@ namespace ts {
if (mainModule.flags & SymbolFlags.Namespace) {
// if module symbol has already been merged - it is safe to use it.
// otherwise clone it
mainModule = mainModule.flags & SymbolFlags.Merged ? mainModule : cloneSymbol(mainModule);
mainModule = mainModule.flags & SymbolFlags.Transient ? mainModule : cloneSymbol(mainModule);
mergeSymbol(mainModule, moduleAugmentation.symbol);
}
else {
@ -585,6 +587,10 @@ namespace ts {
return type.flags & TypeFlags.Object ? (<ObjectType>type).objectFlags : 0;
}
function getCheckFlags(symbol: Symbol): CheckFlags {
return symbol.flags & SymbolFlags.Transient ? (<TransientSymbol>symbol).checkFlags : 0;
}
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(<SourceFile>node);
}
@ -593,7 +599,7 @@ namespace ts {
if (meaning) {
const symbol = symbols.get(name);
if (symbol) {
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (symbol.flags & meaning) {
return symbol;
}
@ -1427,7 +1433,7 @@ namespace ts {
else {
Debug.fail("Unknown entity name kind.");
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
}
@ -1671,23 +1677,7 @@ namespace ts {
}
function symbolIsValue(symbol: Symbol): boolean {
// 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) {
return symbolIsValue(getSymbolLinks(symbol).target);
}
// If the symbol has the value flag, it is trivially a value.
if (symbol.flags & SymbolFlags.Value) {
return true;
}
// If it is an alias, then it is a value if the symbol it resolves to is a value.
if (symbol.flags & SymbolFlags.Alias) {
return (resolveAlias(symbol).flags & SymbolFlags.Value) !== 0;
}
return false;
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value);
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration {
@ -2243,7 +2233,7 @@ namespace ts {
if (parentSymbol) {
// Write type arguments of instantiated class/interface here
if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) {
if (symbol.flags & SymbolFlags.Instantiated) {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
buildDisplayForTypeArgumentsAndDelimiters(getTypeParametersOfClassOrInterface(parentSymbol),
(<TransientSymbol>symbol).mapper, writer, enclosingDeclaration);
}
@ -3437,8 +3427,8 @@ namespace ts {
}
const text = getTextOfPropertyName(name);
const flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = <TransientSymbol>createSymbol(flags, text);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
symbol.bindingElement = e;
members.set(symbol.name, symbol);
@ -3736,7 +3726,7 @@ namespace ts {
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & SymbolFlags.Instantiated) {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
@ -3761,9 +3751,9 @@ namespace ts {
return getObjectFlags(type) & ObjectFlags.Reference ? (<TypeReference>type).target : type;
}
function hasBaseType(type: BaseType, checkBase: BaseType) {
function hasBaseType(type: Type, checkBase: Type) {
return check(type);
function check(type: BaseType): boolean {
function check(type: Type): boolean {
if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
const target = <InterfaceType>getTargetType(type);
return target === checkBase || forEach(getBaseTypes(target), check);
@ -4534,7 +4524,7 @@ namespace ts {
s = cloneSignature(signature);
if (forEach(unionSignatures, sig => sig.thisParameter)) {
const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true);
s.thisParameter = createTransientSymbol(signature.thisParameter, thisType);
s.thisParameter = createSymbolWithType(signature.thisParameter, thisType);
}
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
@ -4731,9 +4721,9 @@ namespace ts {
const propName = (<LiteralType>t).text;
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName);
prop.checkFlags = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0;
prop.type = propType;
prop.isReadonly = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp);
if (propertySymbol) {
prop.mappedTypeOrigin = propertySymbol;
}
@ -4964,18 +4954,19 @@ namespace ts {
}
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
const types = containingType.types;
const excludeModifiers = containingType.flags & TypeFlags.Union ? ModifierFlags.Private | ModifierFlags.Protected : 0;
let props: Symbol[];
const types = containingType.types;
const isUnion = containingType.flags & TypeFlags.Union;
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
// Flags we want to propagate to the result if they exist in all source symbols
let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
let isReadonly = false;
let isPartial = false;
let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
let checkFlags = CheckFlags.SyntheticProperty;
for (const current of types) {
const type = getApparentType(current);
if (type !== unknownType) {
const prop = getPropertyOfType(type, name);
if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & excludeModifiers)) {
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
if (prop && !(modifiers & excludeModifiers)) {
commonFlags &= prop.flags;
if (!props) {
props = [prop];
@ -4983,26 +4974,26 @@ namespace ts {
else if (!contains(props, prop)) {
props.push(prop);
}
if (isReadonlySymbol(prop)) {
isReadonly = true;
}
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
(!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
(modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) |
(modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) |
(modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0);
}
else if (containingType.flags & TypeFlags.Union) {
isPartial = true;
else if (isUnion) {
checkFlags |= CheckFlags.Partial;
}
}
}
if (!props) {
return undefined;
}
if (props.length === 1 && !isPartial) {
if (props.length === 1 && !(checkFlags & CheckFlags.Partial)) {
return props[0];
}
const propTypes: Type[] = [];
const declarations: Declaration[] = [];
let commonType: Type = undefined;
let hasNonUniformType = false;
for (const prop of props) {
if (prop.declarations) {
addRange(declarations, prop.declarations);
@ -5012,17 +5003,15 @@ namespace ts {
commonType = type;
}
else if (type !== commonType) {
hasNonUniformType = true;
checkFlags |= CheckFlags.HasNonUniformType;
}
propTypes.push(type);
}
const result = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | commonFlags, name);
const result = createSymbol(SymbolFlags.Property | commonFlags, name);
result.checkFlags = checkFlags;
result.containingType = containingType;
result.hasNonUniformType = hasNonUniformType;
result.isPartial = isPartial;
result.declarations = declarations;
result.isReadonly = isReadonly;
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
return result;
}
@ -5046,7 +5035,7 @@ namespace ts {
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
const property = getUnionOrIntersectionProperty(type, name);
// We need to filter out partial properties in union types
return property && !(property.flags & SymbolFlags.SyntheticProperty && (<TransientSymbol>property).isPartial) ? property : undefined;
return property && !(getCheckFlags(property) & CheckFlags.Partial) ? property : undefined;
}
/**
@ -5852,7 +5841,7 @@ namespace ts {
for (let i = 0; i < arity; i++) {
const typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter);
typeParameters.push(typeParameter);
const property = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i);
const property = createSymbol(SymbolFlags.Property, "" + i);
property.type = typeParameter;
properties.push(property);
}
@ -6098,7 +6087,10 @@ namespace ts {
if (type.flags & TypeFlags.Union && typeSet.unionIndex === undefined) {
typeSet.unionIndex = typeSet.length;
}
typeSet.push(type);
if (!(type.flags & TypeFlags.Object && (<ObjectType>type).objectFlags & ObjectFlags.Anonymous &&
type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
typeSet.push(type);
}
}
}
@ -6436,13 +6428,13 @@ namespace ts {
const rightType = getTypeOfSymbol(rightProp);
if (maybeTypeOfKind(rightType, TypeFlags.Undefined) || rightProp.flags & SymbolFlags.Optional) {
const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations);
const flags = SymbolFlags.Property | SymbolFlags.Transient | (leftProp.flags & SymbolFlags.Optional);
const result = <TransientSymbol>createSymbol(flags, leftProp.name);
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
const result = createSymbol(flags, leftProp.name);
result.checkFlags = isReadonlySymbol(leftProp) || isReadonlySymbol(rightProp) ? CheckFlags.Readonly : 0;
result.type = getUnionType([getTypeOfSymbol(leftProp), getTypeWithFacts(rightType, TypeFacts.NEUndefined)]);
result.leftSpread = leftProp;
result.rightSpread = rightProp;
result.declarations = declarations;
result.isReadonly = isReadonlySymbol(leftProp) || isReadonlySymbol(rightProp);
members.set(leftProp.name, result);
}
}
@ -6769,7 +6761,7 @@ namespace ts {
}
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol {
if (symbol.flags & SymbolFlags.Instantiated) {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
const links = getSymbolLinks(symbol);
// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
// type mappers. This ensures that original type identities are properly preserved and that aliases
@ -6777,10 +6769,10 @@ namespace ts {
symbol = links.target;
mapper = combineTypeMappers(links.mapper, mapper);
}
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
// also transient so that we can just store data on it directly.
const result = <TransientSymbol>createSymbol(SymbolFlags.Instantiated | SymbolFlags.Transient | symbol.flags, symbol.name);
const result = createSymbol(symbol.flags, symbol.name);
result.checkFlags = CheckFlags.Instantiated;
result.declarations = symbol.declarations;
result.parent = symbol.parent;
result.target = symbol;
@ -6788,7 +6780,6 @@ namespace ts {
if (symbol.valueDeclaration) {
result.valueDeclaration = symbol.valueDeclaration;
}
return result;
}
@ -7983,6 +7974,12 @@ namespace ts {
const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp);
const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp);
if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) {
if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate) {
if (reportErrors) {
reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(sourceProp), typeToString(source));
}
return Ternary.False;
}
if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) {
if (reportErrors) {
if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) {
@ -7998,13 +7995,10 @@ namespace ts {
}
}
else if (targetPropFlags & ModifierFlags.Protected) {
const sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & SymbolFlags.Class;
const sourceClass = sourceDeclaredInClass ? <InterfaceType>getDeclaredTypeOfSymbol(getParentOfSymbol(sourceProp)) : undefined;
const targetClass = <InterfaceType>getDeclaredTypeOfSymbol(getParentOfSymbol(targetProp));
if (!sourceClass || !hasBaseType(sourceClass, targetClass)) {
if (!isValidOverrideOf(sourceProp, targetProp)) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2,
symbolToString(targetProp), typeToString(sourceClass || source), typeToString(targetClass));
reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp),
typeToString(getDeclaringClass(sourceProp) || source), typeToString(getDeclaringClass(targetProp) || target));
}
return Ternary.False;
}
@ -8246,6 +8240,49 @@ namespace ts {
}
}
// Invoke the callback for each underlying property symbol of the given symbol and return the first
// value that isn't undefined.
function forEachProperty<T>(prop: Symbol, callback: (p: Symbol) => T): T {
if (getCheckFlags(prop) & CheckFlags.SyntheticProperty) {
for (const t of (<TransientSymbol>prop).containingType.types) {
const p = getPropertyOfType(t, prop.name);
const result = p && forEachProperty(p, callback);
if (result) {
return result;
}
}
return undefined;
}
return callback(prop);
}
// Return the declaring class type of a property or undefined if property not declared in class
function getDeclaringClass(prop: Symbol) {
return prop.parent && prop.parent.flags & SymbolFlags.Class ? getDeclaredTypeOfSymbol(getParentOfSymbol(prop)) : undefined;
}
// Return true if some underlying source property is declared in a class that derives
// from the given base class.
function isPropertyInClassDerivedFrom(prop: Symbol, baseClass: Type) {
return forEachProperty(prop, sp => {
const sourceClass = getDeclaringClass(sp);
return sourceClass ? hasBaseType(sourceClass, baseClass) : false;
});
}
// Return true if source property is a valid override of protected parts of target property.
function isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) {
return !forEachProperty(targetProp, tp => getDeclarationModifierFlagsFromSymbol(tp) & ModifierFlags.Protected ?
!isPropertyInClassDerivedFrom(sourceProp, getDeclaringClass(tp)) : false);
}
// Return true if the given class derives from each of the declaring classes of the protected
// constituents of the given property.
function isClassDerivedFromDeclaringClasses(checkClass: Type, prop: Symbol) {
return forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p) & ModifierFlags.Protected ?
!hasBaseType(checkClass, getDeclaringClass(p)) : false) ? undefined : checkClass;
}
// Return true if the given type is the constructor type for an abstract class
function isAbstractConstructorType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Anonymous) {
@ -8578,8 +8615,8 @@ namespace ts {
getSignaturesOfType(type, SignatureKind.Construct).length === 0;
}
function createTransientSymbol(source: Symbol, type: Type) {
const symbol = <TransientSymbol>createSymbol(source.flags | SymbolFlags.Transient, source.name);
function createSymbolWithType(source: Symbol, type: Type) {
const symbol = createSymbol(source.flags, source.name);
symbol.declarations = source.declarations;
symbol.parent = source.parent;
symbol.type = type;
@ -8595,7 +8632,7 @@ namespace ts {
for (const property of getPropertiesOfObjectType(type)) {
const original = getTypeOfSymbol(property);
const updated = f(original);
members.set(property.name, updated === original ? property : createTransientSymbol(property, updated));
members.set(property.name, updated === original ? property : createSymbolWithType(property, updated));
};
return members;
}
@ -8833,10 +8870,10 @@ namespace ts {
if (!inferredPropType) {
return undefined;
}
const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & optionalMask, prop.name);
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.name);
inferredProp.checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
inferredProp.declarations = prop.declarations;
inferredProp.type = inferredPropType;
inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop);
members.set(prop.name, inferredProp);
}
if (indexInfo) {
@ -9318,9 +9355,9 @@ namespace ts {
function isDiscriminantProperty(type: Type, name: string) {
if (type && type.flags & TypeFlags.Union) {
const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) {
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
(<TransientSymbol>prop).isDiscriminantProperty = (<TransientSymbol>prop).hasNonUniformType && isLiteralType(getTypeOfSymbol(prop));
(<TransientSymbol>prop).isDiscriminantProperty = (<TransientSymbol>prop).checkFlags & CheckFlags.HasNonUniformType && isLiteralType(getTypeOfSymbol(prop));
}
return (<TransientSymbol>prop).isDiscriminantProperty;
}
@ -11785,7 +11822,7 @@ namespace ts {
}
typeFlags |= type.flags;
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
const prop = createSymbol(SymbolFlags.Property | member.flags, member.name);
if (inDestructuringPattern) {
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
// for the property, make the property optional.
@ -12410,7 +12447,22 @@ namespace ts {
}
function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags {
return s.valueDeclaration ? getCombinedModifierFlags(s.valueDeclaration) : s.flags & SymbolFlags.Prototype ? ModifierFlags.Public | ModifierFlags.Static : 0;
if (s.valueDeclaration) {
const flags = getCombinedModifierFlags(s.valueDeclaration);
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
}
if (getCheckFlags(s) & CheckFlags.SyntheticProperty) {
const checkFlags = (<TransientSymbol>s).checkFlags;
const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
ModifierFlags.Protected;
const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0;
return accessModifier | staticModifier;
}
if (s.flags & SymbolFlags.Prototype) {
return ModifierFlags.Public | ModifierFlags.Static;
}
return 0;
}
function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags {
@ -12425,12 +12477,18 @@ namespace ts {
* @param type The type of left.
* @param prop The symbol for the right hand side of the property access.
*/
function checkClassPropertyAccess(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean {
function checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean {
const flags = getDeclarationModifierFlagsFromSymbol(prop);
const declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(getParentOfSymbol(prop));
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ?
(<PropertyAccessExpression | VariableDeclaration>node).name :
(<QualifiedName>node).right;
if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) {
// Synthetic property with private constituent property
error(errorNode, Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(prop), typeToString(type));
return false;
}
if (left.kind === SyntaxKind.SuperKeyword) {
// TS 1.0 spec (April 2014): 4.8.2
// - In a constructor, instance member function, instance member accessor, or
@ -12449,14 +12507,12 @@ namespace ts {
return false;
}
}
if (flags & ModifierFlags.Abstract) {
// A method cannot be accessed in a super property access if the method is abstract.
// This error could mask a private property access error. But, a member
// cannot simultaneously be private and abstract, so this will trigger an
// additional error elsewhere.
error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, symbolToString(prop), typeToString(declaringClass));
error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
}
@ -12472,7 +12528,7 @@ namespace ts {
if (flags & ModifierFlags.Private) {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop));
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(declaringClass));
error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
return true;
@ -12485,15 +12541,15 @@ namespace ts {
return true;
}
// Get the enclosing class that has the declaring class as its base type
// Find the first enclosing class that has the declaring classes of the protected constituents
// of the property as base classes
const enclosingClass = forEachEnclosingClass(node, enclosingDeclaration => {
const enclosingClass = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration));
return hasBaseType(enclosingClass, declaringClass) ? enclosingClass : undefined;
return isClassDerivedFromDeclaringClasses(enclosingClass, prop) ? enclosingClass : undefined;
});
// A protected property is accessible if the property is within the declaring class or classes derived from it
if (!enclosingClass) {
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(declaringClass));
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(getDeclaringClass(prop) || type));
return false;
}
// No further restrictions for static properties
@ -12505,9 +12561,7 @@ namespace ts {
// get the original type -- represented as the type constraint of the 'this' type
type = getConstraintOfTypeParameter(<TypeParameter>type);
}
// TODO: why is the first part of this check here?
if (!(getObjectFlags(getTargetType(type)) & ObjectFlags.ClassOrInterface && hasBaseType(<InterfaceType>type, enclosingClass))) {
if (!(getObjectFlags(getTargetType(type)) & ObjectFlags.ClassOrInterface && hasBaseType(type, enclosingClass))) {
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass));
return false;
}
@ -12558,9 +12612,8 @@ namespace ts {
noUnusedIdentifiers &&
(prop.flags & SymbolFlags.ClassMember) &&
prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) {
if (prop.flags & SymbolFlags.Instantiated) {
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
getSymbolLinks(prop).target.isReferenced = true;
}
else {
prop.isReferenced = true;
@ -12610,9 +12663,7 @@ namespace ts {
getNodeLinks(node).resolvedSymbol = prop;
if (prop.parent && prop.parent.flags & SymbolFlags.Class) {
checkClassPropertyAccess(node, left, apparentType, prop);
}
checkPropertyAccessibility(node, left, apparentType, prop);
const propType = getTypeOfSymbol(prop);
const assignmentKind = getAssignmentTargetKind(node);
@ -12644,8 +12695,8 @@ namespace ts {
const type = checkExpression(left);
if (type !== unknownType && !isTypeAny(type)) {
const prop = getPropertyOfType(getWidenedType(type), propertyName);
if (prop && prop.parent && prop.parent.flags & SymbolFlags.Class) {
return checkClassPropertyAccess(node, left, type, prop);
if (prop) {
return checkPropertyAccessibility(node, left, type, prop);
}
}
return true;
@ -14139,7 +14190,7 @@ namespace ts {
const parameter = signature.thisParameter;
if (!parameter || parameter.valueDeclaration && !(<ParameterDeclaration>parameter.valueDeclaration).type) {
if (!parameter) {
signature.thisParameter = createTransientSymbol(context.thisParameter, undefined);
signature.thisParameter = createSymbolWithType(context.thisParameter, undefined);
}
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
}
@ -14581,11 +14632,11 @@ namespace ts {
// Get accessors without matching set accessors
// Enum members
// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
return symbol.isReadonly ||
symbol.flags & SymbolFlags.Property && (getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly) !== 0 ||
symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 ||
return !!(getCheckFlags(symbol) & CheckFlags.Readonly ||
symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly ||
symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const ||
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) ||
(symbol.flags & SymbolFlags.EnumMember) !== 0;
symbol.flags & SymbolFlags.EnumMember);
}
function isReferenceToReadonlyEntity(expr: Expression, symbol: Symbol): boolean {
@ -17584,8 +17635,8 @@ namespace ts {
const name = node.propertyName || <Identifier>node.name;
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
markPropertyAsReferenced(property);
if (parent.initializer && property && getParentOfSymbol(property)) {
checkClassPropertyAccess(parent, parent.initializer, parentType, property);
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
}
}
@ -18591,7 +18642,7 @@ namespace ts {
function getTargetSymbol(s: Symbol) {
// if symbol is instantiated its flags are not copied from the 'target'
// so we'll need to get back original 'target' symbol to work with correct set of flags
return s.flags & SymbolFlags.Instantiated ? getSymbolLinks(s).target : s;
return getCheckFlags(s) & CheckFlags.Instantiated ? (<TransientSymbol>s).target : s;
}
function getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration {
@ -19169,7 +19220,7 @@ namespace ts {
// We can detect if augmentation was applied using following rules:
// - augmentation for a global scope is always applied
// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Merged);
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Transient);
if (checkBody && node.body) {
// body of ambient external module is always a module block
for (const statement of (<ModuleBlock>node.body).statements) {
@ -19248,7 +19299,7 @@ namespace ts {
// this is done it two steps
// 1. quick check - if symbol for node is not merged - this is local symbol to this augmentation - report error
// 2. main check - report error if value declaration of the parent symbol is module augmentation)
let reportError = !(symbol.flags & SymbolFlags.Merged);
let reportError = !(symbol.flags & SymbolFlags.Transient);
if (!reportError) {
// symbol should not originate in augmentation
reportError = isExternalModuleAugmentation(symbol.parent.declarations[0]);
@ -20350,7 +20401,7 @@ namespace ts {
}
function getRootSymbols(symbol: Symbol): Symbol[] {
if (symbol.flags & SymbolFlags.SyntheticProperty) {
if (getCheckFlags(symbol) & CheckFlags.SyntheticProperty) {
const symbols: Symbol[] = [];
const name = symbol.name;
forEach(getSymbolLinks(symbol).containingType.types, t => {
@ -21101,7 +21152,7 @@ namespace ts {
function createThenableType() {
// build the thenable type that is used to verify against a non-promise "thenable" operand to `await`.
const thenPropertySymbol = createSymbol(SymbolFlags.Transient | SymbolFlags.Property, "then");
const thenPropertySymbol = createSymbol(SymbolFlags.Property, "then");
getSymbolLinks(thenPropertySymbol).type = globalFunctionType;
const thenableType = <ResolvedType>createObjectType(ObjectFlags.Anonymous);

View file

@ -1787,6 +1787,10 @@
"category": "Error",
"code": 2545
},
"Property '{0}' has conflicting declarations and is inaccessible in type '{1}'.": {
"category": "Error",
"code": 2546
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600

View file

@ -2602,37 +2602,34 @@
export const enum SymbolFlags {
None = 0,
FunctionScopedVariable = 0x00000001, // Variable (var) or parameter
BlockScopedVariable = 0x00000002, // A block-scoped variable (let or const)
Property = 0x00000004, // Property or enum member
EnumMember = 0x00000008, // Enum member
Function = 0x00000010, // Function
Class = 0x00000020, // Class
Interface = 0x00000040, // Interface
ConstEnum = 0x00000080, // Const enum
RegularEnum = 0x00000100, // Enum
ValueModule = 0x00000200, // Instantiated module
NamespaceModule = 0x00000400, // Uninstantiated module
TypeLiteral = 0x00000800, // Type Literal or mapped type
ObjectLiteral = 0x00001000, // Object Literal
Method = 0x00002000, // Method
Constructor = 0x00004000, // Constructor
GetAccessor = 0x00008000, // Get accessor
SetAccessor = 0x00010000, // Set accessor
Signature = 0x00020000, // Call, construct, or index signature
TypeParameter = 0x00040000, // Type parameter
TypeAlias = 0x00080000, // Type alias
ExportValue = 0x00100000, // Exported value marker (see comment in declareModuleMember in binder)
ExportType = 0x00200000, // Exported type marker (see comment in declareModuleMember in binder)
ExportNamespace = 0x00400000, // Exported namespace marker (see comment in declareModuleMember in binder)
Alias = 0x00800000, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
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)
SyntheticProperty = 0x10000000, // Property in union or intersection type
Optional = 0x20000000, // Optional property
ExportStar = 0x40000000, // Export * declaration
FunctionScopedVariable = 1 << 0, // Variable (var) or parameter
BlockScopedVariable = 1 << 1, // A block-scoped variable (let or const)
Property = 1 << 2, // Property or enum member
EnumMember = 1 << 3, // Enum member
Function = 1 << 4, // Function
Class = 1 << 5, // Class
Interface = 1 << 6, // Interface
ConstEnum = 1 << 7, // Const enum
RegularEnum = 1 << 8, // Enum
ValueModule = 1 << 9, // Instantiated module
NamespaceModule = 1 << 10, // Uninstantiated module
TypeLiteral = 1 << 11, // Type Literal or mapped type
ObjectLiteral = 1 << 12, // Object Literal
Method = 1 << 13, // Method
Constructor = 1 << 14, // Constructor
GetAccessor = 1 << 15, // Get accessor
SetAccessor = 1 << 16, // Set accessor
Signature = 1 << 17, // Call, construct, or index signature
TypeParameter = 1 << 18, // Type parameter
TypeAlias = 1 << 19, // Type alias
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder)
ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder)
Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 24, // Prototype property (no source representation)
ExportStar = 1 << 25, // Export * declaration
Optional = 1 << 26, // Optional property
Transient = 1 << 27, // Transient symbol (created during type check)
Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
@ -2692,11 +2689,9 @@
name: string; // Name of symbol
declarations?: Declaration[]; // Declarations associated with this symbol
valueDeclaration?: Declaration; // First value declaration of the symbol
members?: SymbolTable; // Class, interface or literal instance members
exports?: SymbolTable; // Module exports
globalExports?: SymbolTable; // Conditional global UMD exports
/* @internal */ isReadonly?: boolean; // readonly? (set only for intersections and unions)
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
/* @internal */ parent?: Symbol; // Parent symbol
@ -2721,8 +2716,6 @@
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
mappedTypeOrigin?: Symbol; // For a property on a mapped type, points back to the orignal 'T' from 'keyof T'.
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
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
@ -2732,7 +2725,22 @@
}
/* @internal */
export interface TransientSymbol extends Symbol, SymbolLinks { }
export const enum CheckFlags {
Instantiated = 1 << 0, // Instantiated symbol
SyntheticProperty = 1 << 1, // Property in union or intersection type
Readonly = 1 << 2, // Readonly transient symbol
Partial = 1 << 3, // Synthetic property present in some but not all constituents
HasNonUniformType = 1 << 4, // Synthetic property with non-uniform type in constituents
ContainsPublic = 1 << 5, // Synthetic property with public constituent(s)
ContainsProtected = 1 << 6, // Synthetic property with protected constituent(s)
ContainsPrivate = 1 << 7, // Synthetic property with private constituent(s)
ContainsStatic = 1 << 8, // Synthetic property with static constituent(s)
}
/* @internal */
export interface TransientSymbol extends Symbol, SymbolLinks {
checkFlags: CheckFlags;
}
export type SymbolTable = Map<Symbol>;

View file

@ -281,7 +281,7 @@ namespace ts.FindAllReferences {
// if this symbol is visible from its parent container, e.g. exported, then bail out
// if symbol correspond to the union property - bail out
if (symbol.parent || (symbol.flags & SymbolFlags.SyntheticProperty)) {
if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty)) {
return undefined;
}

View file

@ -51,7 +51,7 @@ namespace ts.SymbolDisplay {
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;
if (flags & SymbolFlags.Property) {
if (flags & SymbolFlags.SyntheticProperty) {
if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty) {
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
const rootSymbolFlags = rootSymbol.getFlags();

View file

@ -0,0 +1,156 @@
tests/cases/conformance/classes/mixinAccessModifiers.ts(39,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(43,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(47,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(51,4): error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(66,7): error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
Type 'C1' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(67,7): error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
Type 'C2' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(68,7): error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
Type 'C3' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C3'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(85,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(90,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(98,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(103,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
==== tests/cases/conformance/classes/mixinAccessModifiers.ts (11 errors) ====
type Constructable = new (...args: any[]) => object;
class Private {
constructor (...args: any[]) {}
private p: string;
}
class Private2 {
constructor (...args: any[]) {}
private p: string;
}
class Protected {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Protected2 {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Public {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
class Public2 {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
function f1(x: Private & Private2) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
}
function f2(x: Private & Protected) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
}
function f3(x: Private & Public) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
}
function f4(x: Protected & Protected2) {
x.p; // Error, protected when all constituents are protected
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
}
function f5(x: Protected & Public) {
x.p; // Ok, public if any constituent is public
}
function f6(x: Public & Public2) {
x.p; // Ok, public if any constituent is public
}
declare function Mix<T, U>(c1: T, c2: U): T & U;
// Can't derive from type with inaccessible properties
class C1 extends Mix(Private, Private2) {}
~~
!!! error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
!!! error TS2415: Type 'C1' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
class C2 extends Mix(Private, Protected) {}
~~
!!! error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
!!! error TS2415: Type 'C2' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
class C3 extends Mix(Private, Public) {}
~~
!!! error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
!!! error TS2415: Type 'C3' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C3'.
class C4 extends Mix(Protected, Protected2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p;
c5.p;
c6.p;
}
static g() {
C4.s;
C5.s;
C6.s
}
}
class C5 extends Mix(Protected, Public) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
C5.s;
C6.s
}
}
class C6 extends Mix(Public, Public2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
C5.s;
C6.s
}
}

View file

@ -0,0 +1,323 @@
//// [mixinAccessModifiers.ts]
type Constructable = new (...args: any[]) => object;
class Private {
constructor (...args: any[]) {}
private p: string;
}
class Private2 {
constructor (...args: any[]) {}
private p: string;
}
class Protected {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Protected2 {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Public {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
class Public2 {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
function f1(x: Private & Private2) {
x.p; // Error, private constituent makes property inaccessible
}
function f2(x: Private & Protected) {
x.p; // Error, private constituent makes property inaccessible
}
function f3(x: Private & Public) {
x.p; // Error, private constituent makes property inaccessible
}
function f4(x: Protected & Protected2) {
x.p; // Error, protected when all constituents are protected
}
function f5(x: Protected & Public) {
x.p; // Ok, public if any constituent is public
}
function f6(x: Public & Public2) {
x.p; // Ok, public if any constituent is public
}
declare function Mix<T, U>(c1: T, c2: U): T & U;
// Can't derive from type with inaccessible properties
class C1 extends Mix(Private, Private2) {}
class C2 extends Mix(Private, Protected) {}
class C3 extends Mix(Private, Public) {}
class C4 extends Mix(Protected, Protected2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p;
c5.p;
c6.p;
}
static g() {
C4.s;
C5.s;
C6.s
}
}
class C5 extends Mix(Protected, Public) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s
}
}
class C6 extends Mix(Public, Public2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s
}
}
//// [mixinAccessModifiers.js]
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Private = (function () {
function Private() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Private;
}());
var Private2 = (function () {
function Private2() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Private2;
}());
var Protected = (function () {
function Protected() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Protected;
}());
var Protected2 = (function () {
function Protected2() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Protected2;
}());
var Public = (function () {
function Public() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Public;
}());
var Public2 = (function () {
function Public2() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
return Public2;
}());
function f1(x) {
x.p; // Error, private constituent makes property inaccessible
}
function f2(x) {
x.p; // Error, private constituent makes property inaccessible
}
function f3(x) {
x.p; // Error, private constituent makes property inaccessible
}
function f4(x) {
x.p; // Error, protected when all constituents are protected
}
function f5(x) {
x.p; // Ok, public if any constituent is public
}
function f6(x) {
x.p; // Ok, public if any constituent is public
}
// Can't derive from type with inaccessible properties
var C1 = (function (_super) {
__extends(C1, _super);
function C1() {
return _super !== null && _super.apply(this, arguments) || this;
}
return C1;
}(Mix(Private, Private2)));
var C2 = (function (_super) {
__extends(C2, _super);
function C2() {
return _super !== null && _super.apply(this, arguments) || this;
}
return C2;
}(Mix(Private, Protected)));
var C3 = (function (_super) {
__extends(C3, _super);
function C3() {
return _super !== null && _super.apply(this, arguments) || this;
}
return C3;
}(Mix(Private, Public)));
var C4 = (function (_super) {
__extends(C4, _super);
function C4() {
return _super !== null && _super.apply(this, arguments) || this;
}
C4.prototype.f = function (c4, c5, c6) {
c4.p;
c5.p;
c6.p;
};
C4.g = function () {
C4.s;
C5.s;
C6.s;
};
return C4;
}(Mix(Protected, Protected2)));
var C5 = (function (_super) {
__extends(C5, _super);
function C5() {
return _super !== null && _super.apply(this, arguments) || this;
}
C5.prototype.f = function (c4, c5, c6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
};
C5.g = function () {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s;
};
return C5;
}(Mix(Protected, Public)));
var C6 = (function (_super) {
__extends(C6, _super);
function C6() {
return _super !== null && _super.apply(this, arguments) || this;
}
C6.prototype.f = function (c4, c5, c6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
};
C6.g = function () {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s;
};
return C6;
}(Mix(Public, Public2)));
//// [mixinAccessModifiers.d.ts]
declare type Constructable = new (...args: any[]) => object;
declare class Private {
constructor(...args: any[]);
private p;
}
declare class Private2 {
constructor(...args: any[]);
private p;
}
declare class Protected {
constructor(...args: any[]);
protected p: string;
protected static s: string;
}
declare class Protected2 {
constructor(...args: any[]);
protected p: string;
protected static s: string;
}
declare class Public {
constructor(...args: any[]);
p: string;
static s: string;
}
declare class Public2 {
constructor(...args: any[]);
p: string;
static s: string;
}
declare function f1(x: Private & Private2): void;
declare function f2(x: Private & Protected): void;
declare function f3(x: Private & Public): void;
declare function f4(x: Protected & Protected2): void;
declare function f5(x: Protected & Public): void;
declare function f6(x: Public & Public2): void;
declare function Mix<T, U>(c1: T, c2: U): T & U;
declare class C1 extends Private & Private2 {
}
declare class C2 extends Private & Protected {
}
declare class C3 extends Private & Public {
}
declare class C4 extends Protected & Protected2 {
f(c4: C4, c5: C5, c6: C6): void;
static g(): void;
}
declare class C5 extends Protected & Public {
f(c4: C4, c5: C5, c6: C6): void;
static g(): void;
}
declare class C6 extends Public & Public2 {
f(c4: C4, c5: C5, c6: C6): void;
static g(): void;
}

View file

@ -0,0 +1,116 @@
tests/cases/compiler/mixinPrivateAndProtected.ts(46,3): error TS2445: Property 'ptd' is protected and only accessible within class 'A' and its subclasses.
tests/cases/compiler/mixinPrivateAndProtected.ts(47,3): error TS2341: Property 'pvt' is private and only accessible within class 'A'.
tests/cases/compiler/mixinPrivateAndProtected.ts(50,4): error TS2445: Property 'ptd' is protected and only accessible within class 'mixB<typeof A>.(Anonymous class) & A' and its subclasses.
tests/cases/compiler/mixinPrivateAndProtected.ts(51,4): error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixB<typeof A>.(Anonymous class) & A'.
tests/cases/compiler/mixinPrivateAndProtected.ts(54,5): error TS2445: Property 'ptd' is protected and only accessible within class 'mixC<{ new (...args: any[]): mixB<typeof A>.(Anonymous class); prototype: mixB<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB<typeof A>.(Anonymous class) & A' and its subclasses.
tests/cases/compiler/mixinPrivateAndProtected.ts(55,5): error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixC<{ new (...args: any[]): mixB<typeof A>.(Anonymous class); prototype: mixB<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB<typeof A>.(Anonymous class) & A'.
tests/cases/compiler/mixinPrivateAndProtected.ts(58,6): error TS2445: Property 'ptd' is protected and only accessible within class 'mixC<{ new (...args: any[]): mixB2<typeof A>.(Anonymous class); prototype: mixB2<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB2<typeof A>.(Anonymous class) & A' and its subclasses.
tests/cases/compiler/mixinPrivateAndProtected.ts(59,6): error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixC<{ new (...args: any[]): mixB2<typeof A>.(Anonymous class); prototype: mixB2<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB2<typeof A>.(Anonymous class) & A'.
==== tests/cases/compiler/mixinPrivateAndProtected.ts (8 errors) ====
// Repro from #13830
type Constructor<T> = new(...args: any[]) => T;
class A {
public pb: number = 2;
protected ptd: number = 1;
private pvt: number = 0;
}
function mixB<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
private pvt: number = 0;
};
}
function mixB2<T extends Constructor<A>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
};
}
const
AB = mixB(A),
AB2 = mixB2(A);
function mixC<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 100;
private pvt: number = 0;
};
}
const
AB2C = mixC(AB2),
ABC = mixC(AB);
const
a = new A(),
ab = new AB(),
abc = new ABC(),
ab2c = new AB2C();
a.pb.toFixed();
a.ptd.toFixed(); // Error
~~~
!!! error TS2445: Property 'ptd' is protected and only accessible within class 'A' and its subclasses.
a.pvt.toFixed(); // Error
~~~
!!! error TS2341: Property 'pvt' is private and only accessible within class 'A'.
ab.pb.toFixed();
ab.ptd.toFixed(); // Error
~~~
!!! error TS2445: Property 'ptd' is protected and only accessible within class 'mixB<typeof A>.(Anonymous class) & A' and its subclasses.
ab.pvt.toFixed(); // Error
~~~
!!! error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixB<typeof A>.(Anonymous class) & A'.
abc.pb.toFixed();
abc.ptd.toFixed(); // Error
~~~
!!! error TS2445: Property 'ptd' is protected and only accessible within class 'mixC<{ new (...args: any[]): mixB<typeof A>.(Anonymous class); prototype: mixB<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB<typeof A>.(Anonymous class) & A' and its subclasses.
abc.pvt.toFixed(); // Error
~~~
!!! error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixC<{ new (...args: any[]): mixB<typeof A>.(Anonymous class); prototype: mixB<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB<typeof A>.(Anonymous class) & A'.
ab2c.pb.toFixed();
ab2c.ptd.toFixed(); // Error
~~~
!!! error TS2445: Property 'ptd' is protected and only accessible within class 'mixC<{ new (...args: any[]): mixB2<typeof A>.(Anonymous class); prototype: mixB2<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB2<typeof A>.(Anonymous class) & A' and its subclasses.
ab2c.pvt.toFixed(); // Error
~~~
!!! error TS2546: Property 'pvt' has conflicting declarations and is inaccessible in type 'mixC<{ new (...args: any[]): mixB2<typeof A>.(Anonymous class); prototype: mixB2<any>.(Anonymous class); } & typeof A>.(Anonymous class) & mixB2<typeof A>.(Anonymous class) & A'.
// Repro from #13924
class Person {
constructor(public name: string) {}
protected myProtectedFunction() {
// do something
}
}
function PersonMixin<T extends Constructor<Person>>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
}
myProtectedFunction() {
super.myProtectedFunction();
// do more things
}
};
}
class Customer extends PersonMixin(Person) {
accountBalance: number;
f() {
}
}

View file

@ -0,0 +1,197 @@
//// [mixinPrivateAndProtected.ts]
// Repro from #13830
type Constructor<T> = new(...args: any[]) => T;
class A {
public pb: number = 2;
protected ptd: number = 1;
private pvt: number = 0;
}
function mixB<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
private pvt: number = 0;
};
}
function mixB2<T extends Constructor<A>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
};
}
const
AB = mixB(A),
AB2 = mixB2(A);
function mixC<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 100;
private pvt: number = 0;
};
}
const
AB2C = mixC(AB2),
ABC = mixC(AB);
const
a = new A(),
ab = new AB(),
abc = new ABC(),
ab2c = new AB2C();
a.pb.toFixed();
a.ptd.toFixed(); // Error
a.pvt.toFixed(); // Error
ab.pb.toFixed();
ab.ptd.toFixed(); // Error
ab.pvt.toFixed(); // Error
abc.pb.toFixed();
abc.ptd.toFixed(); // Error
abc.pvt.toFixed(); // Error
ab2c.pb.toFixed();
ab2c.ptd.toFixed(); // Error
ab2c.pvt.toFixed(); // Error
// Repro from #13924
class Person {
constructor(public name: string) {}
protected myProtectedFunction() {
// do something
}
}
function PersonMixin<T extends Constructor<Person>>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
}
myProtectedFunction() {
super.myProtectedFunction();
// do more things
}
};
}
class Customer extends PersonMixin(Person) {
accountBalance: number;
f() {
}
}
//// [mixinPrivateAndProtected.js]
// Repro from #13830
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = (function () {
function A() {
this.pb = 2;
this.ptd = 1;
this.pvt = 0;
}
return A;
}());
function mixB(Cls) {
return (function (_super) {
__extends(class_1, _super);
function class_1() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.ptd = 10;
_this.pvt = 0;
return _this;
}
return class_1;
}(Cls));
}
function mixB2(Cls) {
return (function (_super) {
__extends(class_2, _super);
function class_2() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.ptd = 10;
return _this;
}
return class_2;
}(Cls));
}
var AB = mixB(A), AB2 = mixB2(A);
function mixC(Cls) {
return (function (_super) {
__extends(class_3, _super);
function class_3() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.ptd = 100;
_this.pvt = 0;
return _this;
}
return class_3;
}(Cls));
}
var AB2C = mixC(AB2), ABC = mixC(AB);
var a = new A(), ab = new AB(), abc = new ABC(), ab2c = new AB2C();
a.pb.toFixed();
a.ptd.toFixed(); // Error
a.pvt.toFixed(); // Error
ab.pb.toFixed();
ab.ptd.toFixed(); // Error
ab.pvt.toFixed(); // Error
abc.pb.toFixed();
abc.ptd.toFixed(); // Error
abc.pvt.toFixed(); // Error
ab2c.pb.toFixed();
ab2c.ptd.toFixed(); // Error
ab2c.pvt.toFixed(); // Error
// Repro from #13924
var Person = (function () {
function Person(name) {
this.name = name;
}
Person.prototype.myProtectedFunction = function () {
// do something
};
return Person;
}());
function PersonMixin(Base) {
return (function (_super) {
__extends(class_4, _super);
function class_4() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return _super.apply(this, args) || this;
}
class_4.prototype.myProtectedFunction = function () {
_super.prototype.myProtectedFunction.call(this);
// do more things
};
return class_4;
}(Base));
}
var Customer = (function (_super) {
__extends(Customer, _super);
function Customer() {
return _super !== null && _super.apply(this, arguments) || this;
}
Customer.prototype.f = function () {
};
return Customer;
}(PersonMixin(Person)));

View file

@ -0,0 +1,88 @@
// Repro from #13830
type Constructor<T> = new(...args: any[]) => T;
class A {
public pb: number = 2;
protected ptd: number = 1;
private pvt: number = 0;
}
function mixB<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
private pvt: number = 0;
};
}
function mixB2<T extends Constructor<A>>(Cls: T) {
return class extends Cls {
protected ptd: number = 10;
};
}
const
AB = mixB(A),
AB2 = mixB2(A);
function mixC<T extends Constructor<{}>>(Cls: T) {
return class extends Cls {
protected ptd: number = 100;
private pvt: number = 0;
};
}
const
AB2C = mixC(AB2),
ABC = mixC(AB);
const
a = new A(),
ab = new AB(),
abc = new ABC(),
ab2c = new AB2C();
a.pb.toFixed();
a.ptd.toFixed(); // Error
a.pvt.toFixed(); // Error
ab.pb.toFixed();
ab.ptd.toFixed(); // Error
ab.pvt.toFixed(); // Error
abc.pb.toFixed();
abc.ptd.toFixed(); // Error
abc.pvt.toFixed(); // Error
ab2c.pb.toFixed();
ab2c.ptd.toFixed(); // Error
ab2c.pvt.toFixed(); // Error
// Repro from #13924
class Person {
constructor(public name: string) {}
protected myProtectedFunction() {
// do something
}
}
function PersonMixin<T extends Constructor<Person>>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
}
myProtectedFunction() {
super.myProtectedFunction();
// do more things
}
};
}
class Customer extends PersonMixin(Person) {
accountBalance: number;
f() {
}
}

View file

@ -0,0 +1,108 @@
// @declaration: true
type Constructable = new (...args: any[]) => object;
class Private {
constructor (...args: any[]) {}
private p: string;
}
class Private2 {
constructor (...args: any[]) {}
private p: string;
}
class Protected {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Protected2 {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}
class Public {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
class Public2 {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}
function f1(x: Private & Private2) {
x.p; // Error, private constituent makes property inaccessible
}
function f2(x: Private & Protected) {
x.p; // Error, private constituent makes property inaccessible
}
function f3(x: Private & Public) {
x.p; // Error, private constituent makes property inaccessible
}
function f4(x: Protected & Protected2) {
x.p; // Error, protected when all constituents are protected
}
function f5(x: Protected & Public) {
x.p; // Ok, public if any constituent is public
}
function f6(x: Public & Public2) {
x.p; // Ok, public if any constituent is public
}
declare function Mix<T, U>(c1: T, c2: U): T & U;
// Can't derive from type with inaccessible properties
class C1 extends Mix(Private, Private2) {}
class C2 extends Mix(Private, Protected) {}
class C3 extends Mix(Private, Public) {}
class C4 extends Mix(Protected, Protected2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p;
c5.p;
c6.p;
}
static g() {
C4.s;
C5.s;
C6.s
}
}
class C5 extends Mix(Protected, Public) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s
}
}
class C6 extends Mix(Public, Public2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
C5.s;
C6.s
}
}