diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ea8269f57fd..015c653532b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2332,7 +2332,9 @@ namespace ts { return (type).intrinsicName === "true" ? createTrue() : createFalse(); } if (type.flags & TypeFlags.Unique) { - return createToken(SyntaxKind.SymbolType); + return type.flags & TypeFlags.Fresh + ? createToken(SyntaxKind.SymbolType) + : createTypeQueryNodeFromSymbol(type.symbol); } if (type.flags & TypeFlags.EnumLiteral) { const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); @@ -2962,6 +2964,19 @@ namespace ts { } } + function appendPrototypeOfParentSymbolIfNeeded(parentSymbol: Symbol, symbol: Symbol, writer: SymbolWriter) { + if (parentSymbol.flags & SymbolFlags.Class && + symbol.flags & SymbolFlags.ClassMember && + symbol.valueDeclaration && + !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Static)) { + const prototypeSymbol = parentSymbol.exports && parentSymbol.exports.get("prototype"); + if (prototypeSymbol) { + writePunctuation(writer, SyntaxKind.DotToken); + appendSymbolNameOnly(prototypeSymbol, writer); + } + } + } + /** * Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope * Meaning needs to be specified if the enclosing declaration is given @@ -2980,6 +2995,7 @@ namespace ts { buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration); } } + appendPrototypeOfParentSymbolIfNeeded(parentSymbol, symbol, writer); appendPropertyOrElementAccessForSymbol(symbol, writer); } else { @@ -3084,9 +3100,14 @@ namespace ts { writeAnonymousType(type, nextFlags); } else if (type.flags & TypeFlags.Unique) { - writeKeyword(writer, SyntaxKind.SymbolKeyword); - writePunctuation(writer, SyntaxKind.OpenParenToken); - writePunctuation(writer, SyntaxKind.CloseParenToken); + if (type.flags & TypeFlags.Fresh) { + writeKeyword(writer, SyntaxKind.SymbolKeyword); + writePunctuation(writer, SyntaxKind.OpenParenToken); + writePunctuation(writer, SyntaxKind.CloseParenToken); + } + else { + writeTypeOfSymbol(type.symbol, flags); + } } else if (type.flags & TypeFlags.StringOrNumberLiteral) { writer.writeStringLiteral(literalTypeToString(type)); @@ -3207,10 +3228,10 @@ namespace ts { // Always use 'typeof T' for type of class, enum, and module objects if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) || symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) { - writeTypeOfSymbol(type, flags); + writeTypeOfSymbol(type.symbol, flags); } else if (shouldWriteTypeOfFunctionSymbol()) { - writeTypeOfSymbol(type, flags); + writeTypeOfSymbol(type.symbol, flags); } else if (contains(symbolStack, symbol)) { // If type is an anonymous type literal in a type alias declaration, use type alias name @@ -3255,10 +3276,10 @@ namespace ts { } } - function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) { + function writeTypeOfSymbol(symbol: Symbol, typeFormatFlags?: TypeFormatFlags) { writeKeyword(writer, SyntaxKind.TypeOfKeyword); writeSpace(writer); - buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags); + buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags); } function writePropertyWithModifiers(prop: Symbol) { @@ -4335,6 +4356,12 @@ namespace ts { if (!popTypeResolution()) { type = reportCircularityError(symbol); } + + // If the type is a fresh, unique symbol type and is not the type for this symbol, get its regular type + if (type.flags & TypeFlags.Unique && type.flags & TypeFlags.Fresh && type.symbol !== symbol) { + type = (type).regularType; + } + links.type = type; } return links.type; @@ -7190,7 +7217,7 @@ namespace ts { const remove = t.flags & TypeFlags.StringLiteral && types.containsString || t.flags & TypeFlags.NumberLiteral && types.containsNumber || - t.flags & TypeFlags.StringOrNumberLiteral && t.flags & TypeFlags.FreshLiteral && containsType(types, (t).regularType); + t.flags & TypeFlags.StringOrNumberLiteral && t.flags & TypeFlags.Fresh && containsType(types, (t).regularType); if (remove) { orderedRemoveItemAt(types, i); } @@ -7660,20 +7687,22 @@ namespace ts { return type; } - function getFreshTypeOfLiteralType(type: Type) { - if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) { - if (!(type).freshType) { - const freshType = createLiteralType(type.flags | TypeFlags.FreshLiteral, (type).text); - freshType.regularType = type; - (type).freshType = freshType; + function getFreshTypeOfType(type: Type) { + if (type.flags & (TypeFlags.StringOrNumberLiteral | TypeFlags.Unique) && !(type.flags & TypeFlags.Fresh)) { + if (!(type).freshType) { + const freshType = type.flags & TypeFlags.Unique + ? createUniqueType(type.symbol, TypeFlags.Fresh) + : createLiteralType(type.flags | TypeFlags.Fresh, (type).text); + freshType.regularType = type; + (type).freshType = freshType; } - return (type).freshType; + return (type).freshType; } return type; } - function getRegularTypeOfLiteralType(type: Type) { - return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (type).regularType : type; + function getRegularTypeOfType(type: Type) { + return type.flags & (TypeFlags.StringOrNumberLiteral | TypeFlags.Unique) && type.flags & TypeFlags.Fresh ? (type).regularType : type; } function getLiteralTypeForText(flags: TypeFlags, text: string) { @@ -7688,14 +7717,14 @@ namespace ts { function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getRegularTypeOfLiteralType(checkExpression(node.literal)); + links.resolvedType = getRegularTypeOfType(checkExpression(node.literal)); } return links.resolvedType; } function getTypeFromSymbolTypeNode(node: SymbolTypeNode): Type { const parent = node.parent; - if (parent.kind === SyntaxKind.VariableDeclaration || + if (parent.kind === SyntaxKind.VariableDeclaration || parent.kind === SyntaxKind.PropertyDeclaration || parent.kind === SyntaxKind.PropertySignature || parent.kind === SyntaxKind.PropertyAssignment) { @@ -7705,13 +7734,19 @@ namespace ts { return esSymbolType; } + function getRegularTypeOfUniqueType(type: Type) { + return type.flags & TypeFlags.Unique && type.flags & TypeFlags.Fresh ? (type).regularType : type; + } + + function createUniqueType(symbol: Symbol, flags?: TypeFlags) { + const type = createType(TypeFlags.Unique | flags); + type.symbol = symbol; + return type; + } + function getUniqueTypeForSymbol(symbol: Symbol) { const links = getSymbolLinks(symbol); - if (!links.type) { - links.type = createType(TypeFlags.Unique); - links.type.symbol = symbol; - } - return links.type; + return links.type || (links.type = createUniqueType(symbol)); } function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { @@ -8615,12 +8650,8 @@ namespace ts { } function isTypeRelatedTo(source: Type, target: Type, relation: Map) { - if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { - source = (source).regularType; - } - if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { - target = (target).regularType; - } + source = getRegularTypeOfType(source); + target = getRegularTypeOfType(target); if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) { return true; } @@ -8745,12 +8776,8 @@ namespace ts { */ function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary { let result: Ternary; - if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { - source = (source).regularType; - } - if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { - target = (target).regularType; - } + source = getRegularTypeOfType(source); + target = getRegularTypeOfType(target); // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases if (source === target) return Ternary.True; @@ -8760,7 +8787,7 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; - if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { + if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.Fresh) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { reportRelationError(headMessage, source, target); @@ -9865,8 +9892,8 @@ namespace ts { } function getWidenedLiteralType(type: Type): Type { - return type.flags & TypeFlags.StringLiteral && type.flags & TypeFlags.FreshLiteral ? stringType : - type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? numberType : + return type.flags & TypeFlags.StringLiteral && type.flags & TypeFlags.Fresh ? stringType : + type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.Fresh ? numberType : type.flags & TypeFlags.BooleanLiteral ? booleanType : type.flags & TypeFlags.EnumLiteral ? (type).baseType : type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(sameMap((type).types, getWidenedLiteralType)) : @@ -9962,7 +9989,7 @@ namespace ts { * Leave signatures alone since they are not subject to the check. */ function getRegularTypeOfObjectLiteral(type: Type): Type { - if (!(getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) { + if (!(getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.Fresh)) { return type; } const regularType = (type).regularType; @@ -9978,7 +10005,7 @@ namespace ts { resolved.constructSignatures, resolved.stringIndexInfo, resolved.numberIndexInfo); - regularNew.flags = resolved.flags & ~TypeFlags.FreshLiteral; + regularNew.flags = resolved.flags & ~TypeFlags.Fresh; regularNew.objectFlags |= ObjectFlags.ObjectLiteral; (type).regularType = regularNew; return regularNew; @@ -10981,7 +11008,7 @@ namespace ts { function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { if (clause.kind === SyntaxKind.CaseClause) { - const caseType = getRegularTypeOfLiteralType(getTypeOfExpression((clause).expression)); + const caseType = getRegularTypeOfType(getTypeOfExpression((clause).expression)); return isUnitType(caseType) ? caseType : undefined; } return neverType; @@ -11596,8 +11623,8 @@ namespace ts { return narrowedType.flags & TypeFlags.Never ? type : replacePrimitivesWithLiterals(narrowedType, valueType); } if (isUnitType(valueType)) { - const regularType = getRegularTypeOfLiteralType(valueType); - return filterType(type, t => getRegularTypeOfLiteralType(t) !== regularType); + const regularType = getRegularTypeOfType(valueType); + return filterType(type, t => getRegularTypeOfType(t) !== regularType); } return type; } @@ -11654,7 +11681,7 @@ namespace ts { if (!hasDefaultClause) { return caseType; } - const defaultType = filterType(type, t => !(isUnitType(t) && contains(switchTypes, getRegularTypeOfLiteralType(t)))); + const defaultType = filterType(type, t => !(isUnitType(t) && contains(switchTypes, getRegularTypeOfType(t)))); return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]); } @@ -13341,7 +13368,7 @@ namespace ts { const stringIndexInfo = isJSObjectLiteral ? jsObjectLiteralIndexInfo : hasComputedStringProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.String) : undefined; const numberIndexInfo = hasComputedNumberProperty && !isJSObjectLiteral ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.Number) : undefined; const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); - const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral; + const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.Fresh; result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); result.objectFlags |= ObjectFlags.ObjectLiteral; if (patternWithComputedProperties) { @@ -13517,7 +13544,7 @@ namespace ts { */ function createJsxAttributesType(symbol: Symbol, attributesTable: Map) { const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); - const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral; + const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.Fresh; result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag; result.objectFlags |= ObjectFlags.ObjectLiteral; return result; @@ -15860,15 +15887,15 @@ namespace ts { } const returnType = getReturnTypeOfSignature(signature); - // Treat any call to the global 'Symbol' function that is part of a variable or property + // Treat any call to the global 'Symbol' function that is part of a variable or property // as a fresh unique symbol literal type. if (returnType.flags & TypeFlags.ESSymbolLike && isSymbolOrSymbolForCall(node)) { const parent = skipParentheses(node).parent; - if (parent.kind === SyntaxKind.VariableDeclaration || + if (parent.kind === SyntaxKind.VariableDeclaration || parent.kind === SyntaxKind.PropertyDeclaration || parent.kind === SyntaxKind.PropertyAssignment) { const symbol = getSymbolOfNode(parent); - if (symbol) return getUniqueTypeForSymbol(symbol); + if (symbol) return getFreshTypeOfType(getUniqueTypeForSymbol(symbol)); } } return returnType; @@ -15897,7 +15924,7 @@ namespace ts { if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) { return false; } - + // Make sure require is not a local function const resolvedRequire = resolveName(node.expression, (node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (!resolvedRequire) { @@ -16122,7 +16149,7 @@ namespace ts { const functionFlags = getFunctionFlags(func); let type: Type; if (func.body.kind !== SyntaxKind.Block) { - type = checkExpressionCached(func.body, checkMode); + type = getRegularTypeOfUniqueType(checkExpressionCached(func.body, checkMode)); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the @@ -16162,7 +16189,7 @@ namespace ts { } } // Return a union of the return expression types. - type = getUnionType(types, /*subtypeReduction*/ true); + type = getUnionType(map(types, getRegularTypeOfUniqueType), /*subtypeReduction*/ true); if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function type = functionFlags & FunctionFlags.Async @@ -16175,11 +16202,12 @@ namespace ts { reportErrorsFromWidening(func, type); } - if (isUnitType(type) && - !(contextualSignature && + if (isUnitType(type)) { + if (!(contextualSignature && isLiteralContextualType( contextualSignature === getSignatureFromDeclaration(func) ? type : getReturnTypeOfSignature(contextualSignature)))) { - type = getWidenedLiteralType(type); + type = getWidenedLiteralType(type); + } } const widenedType = getWidenedType(type); @@ -16228,7 +16256,7 @@ namespace ts { if (!switchTypes.length) { return false; } - return eachTypeContainedIn(mapType(type, getRegularTypeOfLiteralType), switchTypes); + return eachTypeContainedIn(mapType(type, getRegularTypeOfType), switchTypes); } function functionHasImplicitReturn(func: FunctionLikeDeclaration) { @@ -16558,7 +16586,7 @@ namespace ts { return silentNeverType; } if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral) { - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -(node.operand).text)); + return getFreshTypeOfType(getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -(node.operand).text)); } switch (node.operator) { case SyntaxKind.PlusToken: @@ -17234,9 +17262,9 @@ namespace ts { } switch (node.kind) { case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.StringLiteral, (node).text)); + return getFreshTypeOfType(getLiteralTypeForText(TypeFlags.StringLiteral, (node).text)); case SyntaxKind.NumericLiteral: - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.NumberLiteral, (node).text)); + return getFreshTypeOfType(getLiteralTypeForText(TypeFlags.NumberLiteral, (node).text)); case SyntaxKind.TrueKeyword: return trueType; case SyntaxKind.FalseKeyword: @@ -22422,7 +22450,7 @@ namespace ts { if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) { expr = expr.parent; } - return getRegularTypeOfLiteralType(getTypeOfExpression(expr)); + return getRegularTypeOfType(getTypeOfExpression(expr)); } /** @@ -22847,6 +22875,9 @@ namespace ts { let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) ? getWidenedLiteralType(getTypeOfSymbol(symbol)) : unknownType; + if (type.flags & TypeFlags.Unique && type.symbol !== symbol) { + type = getRegularTypeOfUniqueType(type); + } if (flags & TypeFormatFlags.AddUndefined) { type = includeFalsyTypes(type, TypeFlags.Undefined); } @@ -22922,7 +22953,7 @@ namespace ts { function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean { if (isConst(node)) { const type = getTypeOfSymbol(getSymbolOfNode(node)); - return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral); + return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.Fresh); } return false; } @@ -23972,7 +24003,7 @@ namespace ts { function checkGrammarSymbolTypeNode(node: SymbolTypeNode) { const parent = node.parent; if (parent.kind !== SyntaxKind.VariableDeclaration && - parent.kind !== SyntaxKind.PropertyDeclaration && + parent.kind !== SyntaxKind.PropertyDeclaration && parent.kind !== SyntaxKind.PropertySignature && parent.kind !== SyntaxKind.PropertyAssignment) { return grammarErrorOnNode(node, Diagnostics.Unique_symbol_types_are_only_allowed_on_variables_and_properties); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4c28598e0f8..7771808c267 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2992,7 +2992,7 @@ namespace ts { Index = 1 << 19, // keyof T IndexedAccess = 1 << 20, // T[K] /* @internal */ - FreshLiteral = 1 << 21, // Fresh literal type + Fresh = 1 << 21, // Fresh literal or unique type /* @internal */ ContainsWideningType = 1 << 22, // Type is or contains undefined or null widening type /* @internal */ @@ -3060,6 +3060,13 @@ namespace ts { regularType?: LiteralType; // Regular version of type } + // Unique symbol types (TypeFlags.Unique) + export interface UniqueType extends Type { + symbol: Symbol; + freshType?: UniqueType; // Fresh version of the type + regularType?: UniqueType; // Regular version of the type + } + // Enum types (TypeFlags.Enum) export interface EnumType extends Type { memberTypes: EnumLiteralType[];