diff --git a/.mailmap b/.mailmap index ede478c8a18..6a9362f6e4c 100644 --- a/.mailmap +++ b/.mailmap @@ -8,11 +8,14 @@ Alexander Rusakov Alex Eagle Anatoly Ressin Anders Hejlsberg unknown unknown +about-code # Andreas Martin Andrej Baran +Andrew Ochsner Andrew Z Allen Andy Hanson Andy Anil Anar Anton Tolmachev +Anubha Mathur anubmat Arnavion # Arnav Singh Arthur Ozga Arthur Ozga Arthur Ozga Arthur Ozga Arthur Ozga Asad Saeeduddin @@ -37,6 +40,7 @@ Dan Corder Dan Quirk Dan Quirk nknown Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser Daniel Rosenwasser David Li +David Sheldrick David Souther Denis Nedelyaev Dick van den Brink unknown unknown @@ -52,6 +56,7 @@ Evan Sebastian Eyas # Eyas Sharaiha Fabian Cook falsandtru # @falsandtru +flowmemo # @flowmemo Frank Wallis František Žiacik František Žiacik Gabe Moothart @@ -62,6 +67,7 @@ Graeme Wicksted Guillaume Salles Guy Bedford guybedford Harald Niesche +Homa Wong Iain Monro Ingvar Stepanyan impinball # Isiah Meadows @@ -81,6 +87,7 @@ Jonathan Park Jonathan Turner Jonathan Turner Jonathan Toland Jesse Schalken +Joel Day Josh Abernathy joshaber Josh Kalderimis Josh Soref @@ -95,10 +102,12 @@ Kanchalai Tanglertsampan Yui T Kanchalai Tanglertsampan Yui Kanchalai Tanglertsampan Yui Kanchalai Tanglertsampan yui T +Kārlis Gaņģis Keith Mashinter kmashint Ken Howard Kevin Lang kimamula # Kenji Imamula +Klaus Meinhardt Kyle Kelley Lorant Pinter Lucien Greathouse @@ -107,6 +116,7 @@ Martin Vseticka Martin Všeticka # Marin Marinov vvakame # Masahiro Wakame Matt McCutchen +MANISH-GIRI # Manish Giri Max Deepfield Micah Zoltu Michael @@ -213,4 +223,6 @@ Tim Perry Vidar Tonaas Fauske Viktor Zozulyak rix # Richard Sentino -rohitverma007 # Rohit Verma \ No newline at end of file +rohitverma007 # Rohit Verma +rdosanjh # Raj Dosanjh +gdh1995 # Dahan Gong \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md index ae7832176ea..196f5e84542 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,13 +12,16 @@ TypeScript is authored by: * Aliaksandr Radzivanovich * Anatoly Ressin * Anders Hejlsberg +* Andreas Martin * Andrej Baran +* Andrew Ochsner * Andrew Z Allen * András Parditka * Andy Hanson * Anil Anar * Anton Khlynovskiy * Anton Tolmachev +* Anubha Mathur * Arnav Singh * Arthur Ozga * Asad Saeeduddin @@ -42,12 +45,14 @@ TypeScript is authored by: * Cotton Hou * Cyrus Najmabadi * Dafrok Zhang +* Dahan Gong * Dan Corder * Dan Quirk * Daniel Hollocher * Daniel Rosenwasser * David Kmenta * David Li +* David Sheldrick * David Souther * Denis Nedelyaev * Dick van den Brink @@ -66,6 +71,7 @@ TypeScript is authored by: * Eyas Sharaiha * Fabian Cook * @falsandtru +* @flowmemo * Frank Wallis * Franklin Tse * František Žiacik @@ -79,6 +85,7 @@ TypeScript is authored by: * Guy Bedford * Harald Niesche * Herrington Darkholme +* Homa Wong * Iain Monro * Ingvar Stepanyan * Isiah Meadows @@ -93,6 +100,7 @@ TypeScript is authored by: * Jeffrey Morlan * Jesse Schalken * Jiri Tobisek +* Joel Day * Joey Wilson * Johannes Rieken * John Vilk @@ -114,10 +122,13 @@ TypeScript is authored by: * Ken Howard * Kenji Imamula * Kevin Lang +* Klaus Meinhardt * Kyle Kelley +* Kārlis Gaņģis * Lorant Pinter * Lucien Greathouse * Lukas Elmer +* Manish Giri * Marin Marinov * Marius Schulz * Martin Vseticka @@ -155,6 +166,7 @@ TypeScript is authored by: * @progre * Punya Biswal * Rado Kirov +* Raj Dosanjh * Richard Knoll * Richard Sentino * Robert Coie diff --git a/Jakefile.js b/Jakefile.js index 3ee8476c842..0b3a26dbb77 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -269,6 +269,7 @@ var harnessSources = harnessCoreSources.concat([ "projectErrors.ts", "matchFiles.ts", "initializeTSConfig.ts", + "printer.ts", ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ba48f38c80..8f847daa679 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2480,7 +2480,8 @@ namespace ts { const symbol = type.symbol; if (symbol) { // Always use 'typeof T' for type of class, enum, and module objects - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { + if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) || + symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) { writeTypeOfSymbol(type, flags); } else if (shouldWriteTypeOfFunctionSymbol()) { @@ -3661,6 +3662,11 @@ namespace ts { return links.type; } + function getBaseTypeVariableOfClass(symbol: Symbol) { + const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol)); + return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined; + } + function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { @@ -3669,8 +3675,13 @@ namespace ts { } else { const type = createObjectType(ObjectFlags.Anonymous, symbol); - links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? - includeFalsyTypes(type, TypeFlags.Undefined) : type; + if (symbol.flags & SymbolFlags.Class) { + const baseTypeVariable = getBaseTypeVariableOfClass(symbol); + links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type; + } + else { + links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type; + } } } return links.type; @@ -3834,8 +3845,26 @@ namespace ts { return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)); } + // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single + // rest parameter of type any[]. + function isMixinConstructorType(type: Type) { + const signatures = getSignaturesOfType(type, SignatureKind.Construct); + if (signatures.length === 1) { + const s = signatures[0]; + return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && getTypeOfParameter(s.parameters[0]) === anyArrayType; + } + return false; + } + function isConstructorType(type: Type): boolean { - return isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0; + if (isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0) { + return true; + } + if (type.flags & TypeFlags.TypeVariable) { + const constraint = getBaseConstraintOfType(type); + return isValidBaseType(constraint) && isMixinConstructorType(constraint); + } + return false; } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { @@ -3914,7 +3943,7 @@ namespace ts { function resolveBaseTypesOfClass(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; - const baseConstructorType = getBaseConstructorTypeOfClass(type); + const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection))) { return; } @@ -3984,7 +4013,7 @@ namespace ts { // 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) || + return type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive) && !isGenericMappedType(type) || type.flags & TypeFlags.Intersection && !forEach((type).types, t => !isValidBaseType(t)); } @@ -4565,16 +4594,47 @@ namespace ts { getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly); } + function includeMixinType(type: Type, types: Type[], index: number): Type { + const mixedTypes: Type[] = []; + for (let i = 0; i < types.length; i++) { + if (i === index) { + mixedTypes.push(type); + } + else if (isMixinConstructorType(types[i])) { + mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0])); + } + } + return getIntersectionType(mixedTypes); + } + function resolveIntersectionTypeMembers(type: IntersectionType) { // The members and properties collections are empty for intersection types. To get all properties of an // intersection type use getPropertiesOfType (only the language service uses this). let callSignatures: Signature[] = emptyArray; let constructSignatures: Signature[] = emptyArray; - let stringIndexInfo: IndexInfo = undefined; - let numberIndexInfo: IndexInfo = undefined; - for (const t of type.types) { + let stringIndexInfo: IndexInfo; + let numberIndexInfo: IndexInfo; + const types = type.types; + const mixinCount = countWhere(types, isMixinConstructorType); + for (let i = 0; i < types.length; i++) { + const t = type.types[i]; + // When an intersection type contains mixin constructor types, the construct signatures from + // those types are discarded and their return types are mixed into the return types of all + // other construct signatures in the intersection type. For example, the intersection type + // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature + // 'new(s: string) => A & B'. + if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(t)) { + let signatures = getSignaturesOfType(t, SignatureKind.Construct); + if (signatures.length && mixinCount > 0) { + signatures = map(signatures, s => { + const clone = cloneSignature(s); + clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, i); + return clone; + }); + } + constructSignatures = concatenate(constructSignatures, signatures); + } callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); - constructSignatures = concatenate(constructSignatures, getSignaturesOfType(t, SignatureKind.Construct)); stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } @@ -4616,7 +4676,7 @@ namespace ts { constructSignatures = getDefaultConstructSignatures(classType); } const baseConstructorType = getBaseConstructorTypeOfClass(classType); - if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) { + if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) { members = createSymbolTable(getNamedMembers(members)); addInheritedMembers(members, getPropertiesOfType(baseConstructorType)); } @@ -4892,7 +4952,7 @@ namespace ts { } function getApparentTypeOfIntersectionType(type: IntersectionType) { - return type.resolvedIndexType || (type.resolvedApparentType = getTypeWithThisArgument(type, type)); + return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type)); } /** @@ -4930,12 +4990,13 @@ namespace ts { t.flags & TypeFlags.NumberLike ? globalNumberType : t.flags & TypeFlags.BooleanLike ? globalBooleanType : t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType() : - t.flags & TypeFlags.NonPrimitive ? globalObjectType : + t.flags & TypeFlags.NonPrimitive ? emptyObjectType : t; } 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[]; // 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; @@ -4945,7 +5006,7 @@ namespace ts { const type = getApparentType(current); if (type !== unknownType) { const prop = getPropertyOfType(type, name); - if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))) { + if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & excludeModifiers)) { commonFlags &= prop.flags; if (!props) { props = [prop]; @@ -6926,6 +6987,11 @@ namespace ts { } } break; + case SyntaxKind.MappedType: + if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter)))) { + return true; + } + break; case SyntaxKind.JSDocFunctionType: const func = node as JSDocFunctionType; for (const p of func.parameters) { @@ -7088,9 +7154,12 @@ namespace ts { result.properties = resolved.properties; result.callSignatures = emptyArray; result.constructSignatures = emptyArray; - type = result; + return result; } } + else if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(map((type).types, getTypeWithoutSignatures)); + } return type; } @@ -7741,7 +7810,7 @@ namespace ts { function isKnownProperty(type: Type, name: string): boolean { if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); - if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) || + if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyResolvedType(resolved)) || resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name)) || getPropertyOfType(type, name)) { @@ -7758,7 +7827,7 @@ namespace ts { return false; } - function isEmptyObjectType(t: ResolvedType) { + function isEmptyResolvedType(t: ResolvedType) { return t.properties.length === 0 && t.callSignatures.length === 0 && t.constructSignatures.length === 0 && @@ -7766,6 +7835,10 @@ namespace ts { !t.numberIndexInfo; } + function isEmptyObjectType(type: Type) { + return type.flags & TypeFlags.Object && isEmptyResolvedType(resolveStructuredTypeMembers(type)); + } + function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { for (const prop of getPropertiesOfObjectType(source)) { @@ -7979,10 +8052,14 @@ namespace ts { } } } + else if ((target).declaration.questionToken && isEmptyObjectType(source)) { + return Ternary.True; + + } } else if (relation !== identityRelation) { const resolved = resolveStructuredTypeMembers(target); - if (isEmptyObjectType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) { + if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) { return Ternary.True; } } @@ -18637,7 +18714,8 @@ namespace ts { const baseTypes = getBaseTypes(type); if (baseTypes.length && produceDiagnostics) { const baseType = baseTypes[0]; - const staticBaseType = getBaseConstructorTypeOfClass(type); + const baseConstructorType = getBaseConstructorTypeOfClass(type); + const staticBaseType = getApparentType(baseConstructorType); checkBaseTypeAccessibility(staticBaseType, baseTypeNode); checkSourceElement(baseTypeNode.expression); if (baseTypeNode.typeArguments) { @@ -18651,6 +18729,9 @@ namespace ts { checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); + if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) { + error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any); + } if (baseType.symbol && baseType.symbol.valueDeclaration && !isInAmbientContext(baseType.symbol.valueDeclaration) && @@ -18660,7 +18741,7 @@ namespace ts { } } - if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class)) { + if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) { // When the static base type is a "class-like" constructor function (but not actually a class), we verify // that all instantiated base constructor signatures return the same type. We can simply compare the type // references (as opposed to checking the structure of the types) because elsewhere we have already checked diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index bd9190dbec7..200158c2f5c 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,17 +5,16 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + setWriter(writer: EmitTextWriter): void; + emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void; emitTrailingCommentsOfPosition(pos: number): void; } - export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter { - const compilerOptions = host.getCompilerOptions(); - const extendedDiagnostics = compilerOptions.extendedDiagnostics; - const newLine = host.getNewLine(); - const { emitPos } = sourceMap; - + export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter { + const extendedDiagnostics = printerOptions.extendedDiagnostics; + const newLine = getNewLineCharacter(printerOptions); + let writer: EmitTextWriter; let containerPos = -1; let containerEnd = -1; let declarationListContainerEnd = -1; @@ -24,19 +23,20 @@ namespace ts { let currentLineMap: number[]; let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[]; let hasWrittenComment = false; - let disabled: boolean = compilerOptions.removeComments; + let disabled: boolean = printerOptions.removeComments; return { reset, + setWriter, setSourceFile, emitNodeWithComments, emitBodyWithDetachedComments, emitTrailingCommentsOfPosition, }; - function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { if (disabled) { - emitCallback(emitContext, node); + emitCallback(hint, node); return; } @@ -47,11 +47,11 @@ namespace ts { // Both pos and end are synthesized, so just emit the node without comments. if (emitFlags & EmitFlags.NoNestedComments) { disabled = true; - emitCallback(emitContext, node); + emitCallback(hint, node); disabled = false; } else { - emitCallback(emitContext, node); + emitCallback(hint, node); } } else { @@ -94,11 +94,11 @@ namespace ts { if (emitFlags & EmitFlags.NoNestedComments) { disabled = true; - emitCallback(emitContext, node); + emitCallback(hint, node); disabled = false; } else { - emitCallback(emitContext, node); + emitCallback(hint, node); } if (extendedDiagnostics) { @@ -198,9 +198,9 @@ namespace ts { } // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitPos(commentPos); + if (emitPos) emitPos(commentPos); writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + if (emitPos) emitPos(commentEnd); if (hasTrailingNewLine) { writer.writeLine(); @@ -220,9 +220,9 @@ namespace ts { writer.write(" "); } - emitPos(commentPos); + if (emitPos) emitPos(commentPos); writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + if (emitPos) emitPos(commentEnd); if (hasTrailingNewLine) { writer.writeLine(); @@ -248,9 +248,9 @@ namespace ts { function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space - emitPos(commentPos); + if (emitPos) emitPos(commentPos); writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + if (emitPos) emitPos(commentEnd); if (hasTrailingNewLine) { writer.writeLine(); @@ -286,6 +286,10 @@ namespace ts { detachedCommentsInfo = undefined; } + function setWriter(output: EmitTextWriter): void { + writer = output; + } + function setSourceFile(sourceFile: SourceFile) { currentSourceFile = sourceFile; currentText = currentSourceFile.text; @@ -323,9 +327,9 @@ namespace ts { } function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) { - emitPos(commentPos); + if (emitPos) emitPos(commentPos); writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine); - emitPos(commentEnd); + if (emitPos) emitPos(commentEnd); } /** diff --git a/src/compiler/core.ts b/src/compiler/core.ts index de3472afd88..304fba69b26 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1447,7 +1447,7 @@ namespace ts { return /^\.\.?($|[\\/])/.test(moduleName); } - export function getEmitScriptTarget(compilerOptions: CompilerOptions) { + export function getEmitScriptTarget(compilerOptions: CompilerOptions | PrinterOptions) { return compilerOptions.target || ScriptTarget.ES3; } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index bc2e967ede1..a0589daf73a 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -35,13 +35,15 @@ namespace ts { forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile); return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined); - function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) { - emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sources, isBundledEmit, /*emitOnlyDtsFiles*/ false); + function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { + emitDeclarations(host, resolver, declarationDiagnostics, declarationFilePath, sourceFileOrBundle, /*emitOnlyDtsFiles*/ false); } } function emitDeclarations(host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, declarationFilePath: string, - sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean): DeclarationEmit { + sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean): DeclarationEmit { + const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle]; + const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; const newLine = host.getNewLine(); const compilerOptions = host.getCompilerOptions(); @@ -1820,8 +1822,9 @@ namespace ts { } return addedBundledEmitReference; - function getDeclFileName(emitFileNames: EmitFileNames, _sourceFiles: SourceFile[], isBundledEmit: boolean) { + function getDeclFileName(emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { // Dont add reference path to this file if it is a bundled emit and caller asked not emit bundled file path + const isBundledEmit = sourceFileOrBundle.kind === SyntaxKind.Bundle; if (isBundledEmit && !addBundledFileReference) { return; } @@ -1834,10 +1837,11 @@ namespace ts { } /* @internal */ - export function writeDeclarationFile(declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { - const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFiles, isBundledEmit, emitOnlyDtsFiles); + export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { + const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles); const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit; if (!emitSkipped) { + const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle]; const declarationOutput = emitDeclarationResult.referencesOutput + getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo); writeFile(host, emitterDiagnostics, declarationFilePath, declarationOutput, host.getCompilerOptions().emitBOM, sourceFiles); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f831f181f47..a3aba6fd337 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1783,6 +1783,10 @@ "category": "Error", "code": 2544 }, + "A mixin class must have a constructor with a single rest parameter of type 'any[]'.": { + "category": "Error", + "code": 2545 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 27285a4c2dc..498f65ad366 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,61 +1,27 @@ -/// +/// /// -/// -/// +/// +/// /// -/* @internal */ namespace ts { - // Flags enum to track count of temp variables and a few dedicated names - const enum TempFlags { - Auto = 0x00000000, // No preferred name - CountMask = 0x0FFFFFFF, // Temp variable counter - _i = 0x10000000, // Use/preference flag for '_i' - } - - const id = (s: SourceFile) => s; - const nullTransformers: Transformer[] = [_ => id]; + const delimiters = createDelimiterMap(); + const brackets = createBracketsMap(); + /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile, emitOnlyDtsFiles?: boolean): EmitResult { - const delimiters = createDelimiterMap(); - const brackets = createBracketsMap(); const compilerOptions = host.getCompilerOptions(); - const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); const sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; const emittedFilesList: string[] = compilerOptions.listEmittedFiles ? [] : undefined; const emitterDiagnostics = createDiagnosticCollection(); const newLine = host.getNewLine(); - const transformers: Transformer[] = emitOnlyDtsFiles ? nullTransformers : getTransformers(compilerOptions); + const transformers = emitOnlyDtsFiles ? [] : getTransformers(compilerOptions); const writer = createTextWriter(newLine); - const { - write, - writeLine, - increaseIndent, - decreaseIndent - } = writer; - const sourceMap = createSourceMapWriter(host, writer); - const { - emitNodeWithSourceMap, - emitTokenWithSourceMap - } = sourceMap; - const comments = createCommentWriter(host, writer, sourceMap); - const { - emitNodeWithComments, - emitBodyWithDetachedComments, - emitTrailingCommentsOfPosition - } = comments; - - let nodeIdToGeneratedName: string[]; - let autoGeneratedIdToGeneratedName: string[]; - let generatedNameSet: Map; - let tempFlags: TempFlags; let currentSourceFile: SourceFile; - let currentText: string; - let currentFileIdentifiers: Map; let bundledHelpers: Map; let isOwnFileEmit: boolean; let emitSkipped = false; @@ -63,17 +29,30 @@ namespace ts { const sourceFiles = getSourceFilesToEmit(host, targetSourceFile); // Transform the source files - performance.mark("beforeTransform"); - const { - transformed, - emitNodeWithSubstitution, - emitNodeWithNotification - } = transformFiles(resolver, host, sourceFiles, transformers); - performance.measure("transformTime", "beforeTransform"); + const transform = transformFiles(resolver, host, sourceFiles, transformers); + + // Create a printer to print the nodes + const printer = createPrinter(compilerOptions, { + // resolver hooks + hasGlobalName: resolver.hasGlobalName, + + // transform hooks + onEmitNode: transform.emitNodeWithNotification, + onSubstituteNode: transform.emitNodeWithSubstitution, + + // sourcemap hooks + onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, + onEmitSourceMapOfToken: sourceMap.emitTokenWithSourceMap, + onEmitSourceMapOfPosition: sourceMap.emitPos, + + // emitter hooks + onEmitHelpers: emitHelpers, + onSetSourceFile: setSourceFile, + }); // Emit each output file performance.mark("beforePrint"); - forEachEmittedFile(host, emitFile, transformed, emitOnlyDtsFiles); + forEachEmittedFile(host, emitSourceFileOrBundle, transform.transformed, emitOnlyDtsFiles); performance.measure("printTime", "beforePrint"); // Clean up emit nodes on parse tree @@ -88,11 +67,11 @@ namespace ts { sourceMaps: sourceMapDataList }; - function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) { + function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { // Make sure not to write js file and source map file if any of them cannot be written if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { if (!emitOnlyDtsFiles) { - printFile(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, sourceFileOrBundle); } } else { @@ -100,7 +79,7 @@ namespace ts { } if (declarationFilePath) { - emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFiles(sourceFiles), isBundledEmit, host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped; + emitSkipped = writeDeclarationFile(declarationFilePath, getOriginalSourceFileOrBundle(sourceFileOrBundle), host, resolver, emitterDiagnostics, emitOnlyDtsFiles) || emitSkipped; } if (!emitSkipped && emittedFilesList) { @@ -116,34 +95,32 @@ namespace ts { } } - function printFile(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { - sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); - nodeIdToGeneratedName = []; - autoGeneratedIdToGeneratedName = []; - generatedNameSet = createMap(); - bundledHelpers = isBundledEmit ? createMap() : undefined; - isOwnFileEmit = !isBundledEmit; + function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) { + const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; + const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; + const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile]; + sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFileOrBundle); - // Emit helpers from all the files - if (isBundledEmit && moduleKind) { - for (const sourceFile of sourceFiles) { - emitHelpers(sourceFile, /*isBundle*/ true); - } + if (bundle) { + bundledHelpers = createMap(); + isOwnFileEmit = false; + printer.writeBundle(bundle, writer); + } + else { + isOwnFileEmit = true; + printer.writeFile(sourceFile, writer); } - // Print each transformed source file. - forEach(sourceFiles, printSourceFile); - - writeLine(); + writer.writeLine(); const sourceMappingURL = sourceMap.getSourceMappingURL(); if (sourceMappingURL) { - write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment + writer.write(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Sometimes tools can sometimes see this line as a source mapping url comment } // Write the source map if (compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) { - writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles); + writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap.getText(), /*writeByteOrderMark*/ false, sourceFiles); } // Record source map data for the test harness. @@ -156,153 +133,281 @@ namespace ts { // Reset state sourceMap.reset(); - comments.reset(); writer.reset(); - tempFlags = TempFlags.Auto; currentSourceFile = undefined; - currentText = undefined; + bundledHelpers = undefined; isOwnFileEmit = false; } - function printSourceFile(node: SourceFile) { + function setSourceFile(node: SourceFile) { currentSourceFile = node; - currentText = node.text; - currentFileIdentifiers = node.identifiers; sourceMap.setSourceFile(node); - comments.setSourceFile(node); - pipelineEmitWithNotification(EmitContext.SourceFile, node); } - /** - * Emits a node. - */ - function emit(node: Node) { - pipelineEmitWithNotification(EmitContext.Unspecified, node); + function emitHelpers(node: Node, writeLines: (text: string) => void) { + let helpersEmitted = false; + const bundle = node.kind === SyntaxKind.Bundle ? node : undefined; + if (bundle && moduleKind === ModuleKind.None) { + return; + } + + const numNodes = bundle ? bundle.sourceFiles.length : 1; + for (let i = 0; i < numNodes; i++) { + const currentNode = bundle ? bundle.sourceFiles[i] : node; + const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile; + const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined); + const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit; + const helpers = getEmitHelpers(currentNode); + if (helpers) { + for (const helper of stableSort(helpers, compareEmitHelpers)) { + if (!helper.scoped) { + // Skip the helper if it can be skipped and the noEmitHelpers compiler + // option is set, or if it can be imported and the importHelpers compiler + // option is set. + if (shouldSkip) continue; + + // Skip the helper if it can be bundled but hasn't already been emitted and we + // are emitting a bundled module. + if (shouldBundle) { + if (bundledHelpers.get(helper.name)) { + continue; + } + + bundledHelpers.set(helper.name, true); + } + } + else if (bundle) { + // Skip the helper if it is scoped and we are emitting bundled helpers + continue; + } + + writeLines(helper.text); + helpersEmitted = true; + } + } + } + + return helpersEmitted; + } + } + + export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { + const { + hasGlobalName, + onEmitSourceMapOfNode, + onEmitSourceMapOfToken, + onEmitSourceMapOfPosition, + onEmitNode, + onEmitHelpers, + onSetSourceFile, + onSubstituteNode, + } = handlers; + + const newLine = getNewLineCharacter(printerOptions); + const languageVersion = getEmitScriptTarget(printerOptions); + const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition); + const { + emitNodeWithComments, + emitBodyWithDetachedComments, + emitTrailingCommentsOfPosition, + } = comments; + + let currentSourceFile: SourceFile; + let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. + let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. + let generatedNames: Map; // Set of names generated by the NameGenerator. + let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. + let tempFlags: TempFlags; // TempFlags for the current name generation scope. + let writer: EmitTextWriter; + let ownWriter: EmitTextWriter; + + reset(); + return { + // public API + printNode, + printFile, + printBundle, + + // internal API + writeNode, + writeFile, + writeBundle + }; + + function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { + switch (hint) { + case EmitHint.SourceFile: + Debug.assert(isSourceFile(node), "Expected a SourceFile node."); + break; + case EmitHint.IdentifierName: + Debug.assert(isIdentifier(node), "Expected an Identifier node."); + break; + case EmitHint.Expression: + Debug.assert(isExpression(node), "Expected an Expression node."); + break; + } + switch (node.kind) { + case SyntaxKind.SourceFile: return printFile(node); + case SyntaxKind.Bundle: return printBundle(node); + } + writeNode(hint, node, sourceFile, beginPrint()); + return endPrint(); + } + + function printBundle(bundle: Bundle): string { + writeBundle(bundle, beginPrint()); + return endPrint(); + } + + function printFile(sourceFile: SourceFile): string { + writeFile(sourceFile, beginPrint()); + return endPrint(); + } + + function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + print(hint, node, sourceFile); + reset(); + writer = previousWriter; + } + + function writeBundle(bundle: Bundle, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + emitHelpersIndirect(bundle); + for (const sourceFile of bundle.sourceFiles) { + print(EmitHint.SourceFile, sourceFile, sourceFile); + } + reset(); + writer = previousWriter; + } + + function writeFile(sourceFile: SourceFile, output: EmitTextWriter) { + const previousWriter = writer; + setWriter(output); + print(EmitHint.SourceFile, sourceFile, sourceFile); + reset(); + writer = previousWriter; + } + + function beginPrint() { + return ownWriter || (ownWriter = createTextWriter(newLine)); + } + + function endPrint() { + const text = ownWriter.getText(); + ownWriter.reset(); + return text; + } + + function print(hint: EmitHint, node: Node, sourceFile: SourceFile) { + setSourceFile(sourceFile); + pipelineEmitWithNotification(hint, node); + } + + function setSourceFile(sourceFile: SourceFile) { + currentSourceFile = sourceFile; + comments.setSourceFile(sourceFile); + if (onSetSourceFile) { + onSetSourceFile(sourceFile); + } + } + + function setWriter(output: EmitTextWriter | undefined) { + writer = output; + comments.setWriter(output); + } + + function reset() { + nodeIdToGeneratedName = []; + autoGeneratedIdToGeneratedName = []; + generatedNames = createMap(); + tempFlagsStack = []; + tempFlags = TempFlags.Auto; + comments.reset(); + setWriter(/*output*/ undefined); + } + + function emit(node: Node, hint = EmitHint.Unspecified) { + pipelineEmitWithNotification(hint, node); } - /** - * Emits an IdentifierName. - */ function emitIdentifierName(node: Identifier) { - pipelineEmitWithNotification(EmitContext.IdentifierName, node); + pipelineEmitWithNotification(EmitHint.IdentifierName, node); } - /** - * Emits an expression node. - */ function emitExpression(node: Expression) { - pipelineEmitWithNotification(EmitContext.Expression, node); + pipelineEmitWithNotification(EmitHint.Expression, node); } - /** - * Emits a node with possible notification. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called from printSourceFile, emit, emitExpression, or - * emitIdentifierName. - */ - function pipelineEmitWithNotification(emitContext: EmitContext, node: Node) { - emitNodeWithNotification(emitContext, node, pipelineEmitWithComments); + function pipelineEmitWithNotification(hint: EmitHint, node: Node) { + if (onEmitNode) { + onEmitNode(hint, node, pipelineEmitWithComments); + } + else { + pipelineEmitWithComments(hint, node); + } } - /** - * Emits a node with comments. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitWithNotification. - */ - function pipelineEmitWithComments(emitContext: EmitContext, node: Node) { - // Do not emit comments for SourceFile - if (emitContext === EmitContext.SourceFile) { - pipelineEmitWithSourceMap(emitContext, node); + function pipelineEmitWithComments(hint: EmitHint, node: Node) { + if (emitNodeWithComments && hint !== EmitHint.SourceFile) { + emitNodeWithComments(hint, node, pipelineEmitWithSourceMap); + } + else { + pipelineEmitWithSourceMap(hint, node); + } + } + + function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { + if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { + onEmitSourceMapOfNode(hint, node, pipelineEmitWithSubstitution); + } + else { + pipelineEmitWithSubstitution(hint, node); + } + } + + function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) { + if (onSubstituteNode) { + onSubstituteNode(hint, node, pipelineEmitWithHint); + } + else { + pipelineEmitWithHint(hint, node); + } + } + + function pipelineEmitWithHint(hint: EmitHint, node: Node): void { + switch (hint) { + case EmitHint.SourceFile: return pipelineEmitSourceFile(node); + case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node); + case EmitHint.Expression: return pipelineEmitExpression(node); + case EmitHint.Unspecified: return pipelineEmitUnspecified(node); + } + } + + function pipelineEmitSourceFile(node: Node): void { + Debug.assertNode(node, isSourceFile); + emitSourceFile(node); + } + + function pipelineEmitIdentifierName(node: Node): void { + Debug.assertNode(node, isIdentifier); + emitIdentifier(node); + } + + function pipelineEmitUnspecified(node: Node): void { + const kind = node.kind; + + // Reserved words + // Strict mode reserved words + // Contextual keywords + if (isKeyword(kind)) { + writeTokenText(kind); return; } - emitNodeWithComments(emitContext, node, pipelineEmitWithSourceMap); - } - - /** - * Emits a node with source maps. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitWithComments. - */ - function pipelineEmitWithSourceMap(emitContext: EmitContext, node: Node) { - // Do not emit source mappings for SourceFile or IdentifierName - if (emitContext === EmitContext.SourceFile - || emitContext === EmitContext.IdentifierName) { - pipelineEmitWithSubstitution(emitContext, node); - return; - } - - emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution); - } - - /** - * Emits a node with possible substitution. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitWithSourceMap or - * pipelineEmitInUnspecifiedContext (when picking a more specific context). - */ - function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) { - emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext); - } - - /** - * Emits a node. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitWithSubstitution. - */ - function pipelineEmitForContext(emitContext: EmitContext, node: Node): void { - switch (emitContext) { - case EmitContext.SourceFile: return pipelineEmitInSourceFileContext(node); - case EmitContext.IdentifierName: return pipelineEmitInIdentifierNameContext(node); - case EmitContext.Unspecified: return pipelineEmitInUnspecifiedContext(node); - case EmitContext.Expression: return pipelineEmitInExpressionContext(node); - } - } - - /** - * Emits a node in the SourceFile EmitContext. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitForContext. - */ - function pipelineEmitInSourceFileContext(node: Node): void { - const kind = node.kind; - switch (kind) { - // Top-level nodes - case SyntaxKind.SourceFile: - return emitSourceFile(node); - } - } - - /** - * Emits a node in the IdentifierName EmitContext. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitForContext. - */ - function pipelineEmitInIdentifierNameContext(node: Node): void { - const kind = node.kind; - switch (kind) { - // Identifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - } - } - - /** - * Emits a node in the Unspecified EmitContext. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitForContext. - */ - function pipelineEmitInUnspecifiedContext(node: Node): void { - const kind = node.kind; switch (kind) { // Pseudo-literals case SyntaxKind.TemplateHead: @@ -314,46 +419,6 @@ namespace ts { case SyntaxKind.Identifier: return emitIdentifier(node); - // Reserved words - case SyntaxKind.ConstKeyword: - case SyntaxKind.DefaultKeyword: - case SyntaxKind.ExportKeyword: - case SyntaxKind.VoidKeyword: - - // Strict mode reserved words - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.PublicKeyword: - case SyntaxKind.StaticKeyword: - - // Contextual keywords - case SyntaxKind.AbstractKeyword: - case SyntaxKind.AsKeyword: - case SyntaxKind.AnyKeyword: - case SyntaxKind.AsyncKeyword: - case SyntaxKind.AwaitKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.ConstructorKeyword: - case SyntaxKind.DeclareKeyword: - case SyntaxKind.GetKeyword: - case SyntaxKind.IsKeyword: - case SyntaxKind.ModuleKeyword: - case SyntaxKind.NamespaceKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.ReadonlyKeyword: - case SyntaxKind.RequireKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.SetKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.TypeKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.FromKeyword: - case SyntaxKind.GlobalKeyword: - case SyntaxKind.OfKeyword: - writeTokenText(kind); - return; - // Parse tree nodes // Names @@ -572,17 +637,11 @@ namespace ts { // If the node is an expression, try to emit it as an expression with // substitution. if (isExpression(node)) { - return pipelineEmitWithSubstitution(EmitContext.Expression, node); + return pipelineEmitWithSubstitution(EmitHint.Expression, node); } } - /** - * Emits a node in the Expression EmitContext. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from pipelineEmitForContext. - */ - function pipelineEmitInExpressionContext(node: Node): void { + function pipelineEmitExpression(node: Node): void { const kind = node.kind; switch (kind) { // Literals @@ -675,6 +734,21 @@ namespace ts { } } + function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void { + if (emitBodyWithDetachedComments) { + emitBodyWithDetachedComments(node, elements, emitCallback); + } + else { + emitCallback(node); + } + } + + function emitHelpersIndirect(node: Node) { + if (onEmitHelpers) { + onEmitHelpers(node, writeLines); + } + } + // // Literals/Pseudo-literals // @@ -695,7 +769,7 @@ namespace ts { // SyntaxKind.TemplateTail function emitLiteral(node: LiteralLikeNode) { const text = getLiteralTextOfNode(node); - if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) + if ((printerOptions.sourceMap || printerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { writer.writeLiteral(text); } @@ -934,17 +1008,13 @@ namespace ts { write("{"); writeLine(); increaseIndent(); - if (node.readonlyToken) { - write("readonly "); - } + writeIfPresent(node.readonlyToken, "readonly "); write("["); emit(node.typeParameter.name); write(" in "); emit(node.typeParameter.constraint); write("]"); - if (node.questionToken) { - write("?"); - } + writeIfPresent(node.questionToken, "?"); write(": "); emit(node.type); write(";"); @@ -1033,7 +1103,7 @@ namespace ts { let indentAfterDot = false; if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; - const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1; + const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); @@ -1066,7 +1136,7 @@ namespace ts { // isFinite handles cases when constantValue is undefined return isFinite(constantValue) && Math.floor(constantValue) === constantValue - && compilerOptions.removeComments; + && printerOptions.removeComments; } } @@ -1097,12 +1167,9 @@ namespace ts { } function emitTypeAssertionExpression(node: TypeAssertion) { - if (node.type) { - write("<"); - emit(node.type); - write(">"); - } - + write("<"); + emit(node.type); + write(">"); emitExpression(node.expression); } @@ -1502,11 +1569,10 @@ namespace ts { emitBlockFunctionBody(body); } else { - const savedTempFlags = tempFlags; - tempFlags = 0; + pushNameGenerationScope(); emitSignatureHead(node); emitBlockFunctionBody(body); - tempFlags = savedTempFlags; + popNameGenerationScope(); } if (indentedFlag) { @@ -1574,10 +1640,11 @@ namespace ts { write(" {"); increaseIndent(); - emitBodyWithDetachedComments(body, body.statements, - shouldEmitBlockFunctionBodyOnSingleLine(body) - ? emitBlockFunctionBodyOnSingleLine - : emitBlockFunctionBodyWorker); + const emitBlockFunctionBody = shouldEmitBlockFunctionBodyOnSingleLine(body) + ? emitBlockFunctionBodyOnSingleLine + : emitBlockFunctionBodyWorker; + + emitBodyIndirect(body, body.statements, emitBlockFunctionBody); decreaseIndent(); writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body); @@ -1590,9 +1657,9 @@ namespace ts { function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) { // Emit all the prologue directives (like "use strict"). const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true); - const helpersEmitted = emitHelpers(body); - - if (statementOffset === 0 && !helpersEmitted && emitBlockFunctionBodyOnSingleLine) { + const pos = writer.getTextPos(); + emitHelpersIndirect(body); + if (statementOffset === 0 && pos === writer.getTextPos() && emitBlockFunctionBodyOnSingleLine) { decreaseIndent(); emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements); increaseIndent(); @@ -1620,18 +1687,15 @@ namespace ts { emitTypeParameters(node, node.typeParameters); emitList(node, node.heritageClauses, ListFormat.ClassHeritageClauses); - const savedTempFlags = tempFlags; - tempFlags = 0; - + pushNameGenerationScope(); write(" {"); emitList(node, node.members, ListFormat.ClassMembers); write("}"); + popNameGenerationScope(); if (indentedFlag) { decreaseIndent(); } - - tempFlags = savedTempFlags; } function emitInterfaceDeclaration(node: InterfaceDeclaration) { @@ -1661,14 +1725,11 @@ namespace ts { emitModifiers(node, node.modifiers); write("enum "); emit(node.name); - - const savedTempFlags = tempFlags; - tempFlags = 0; - + pushNameGenerationScope(); write(" {"); emitList(node, node.members, ListFormat.EnumMembers); write("}"); - tempFlags = savedTempFlags; + popNameGenerationScope(); } function emitModuleDeclaration(node: ModuleDeclaration) { @@ -1692,13 +1753,12 @@ namespace ts { write("{ }"); } else { - const savedTempFlags = tempFlags; - tempFlags = 0; + pushNameGenerationScope(); write("{"); increaseIndent(); emitBlockStatements(node); write("}"); - tempFlags = savedTempFlags; + popNameGenerationScope(); } } @@ -1923,7 +1983,6 @@ namespace ts { } function emitCatchClause(node: CatchClause) { - writeLine(); const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); write(" "); writeToken(SyntaxKind.OpenParenToken, openParenPos); @@ -1948,11 +2007,10 @@ namespace ts { // "comment1" is not considered to be leading comment for node.initializer // but rather a trailing comment on the previous node. const initializer = node.initializer; - if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + if (emitTrailingCommentsOfPosition && (getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { const commentRange = getCommentRange(initializer); emitTrailingCommentsOfPosition(commentRange.pos); } - emitExpression(initializer); } @@ -1987,17 +2045,16 @@ namespace ts { function emitSourceFile(node: SourceFile) { writeLine(); emitShebang(); - emitBodyWithDetachedComments(node, node.statements, emitSourceFileWorker); + emitBodyIndirect(node, node.statements, emitSourceFileWorker); } function emitSourceFileWorker(node: SourceFile) { const statements = node.statements; const statementOffset = emitPrologueDirectives(statements); - const savedTempFlags = tempFlags; - tempFlags = 0; - emitHelpers(node); + pushNameGenerationScope(); + emitHelpersIndirect(node); emitList(node, statements, ListFormat.MultiLine, statementOffset); - tempFlags = savedTempFlags; + popNameGenerationScope(); } // Transformation nodes @@ -2027,83 +2084,12 @@ namespace ts { return statements.length; } - function emitHelpers(node: Node, isBundle?: boolean) { - const sourceFile = isSourceFile(node) ? node : currentSourceFile; - const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined); - const shouldBundle = isSourceFile(node) && !isOwnFileEmit; - - let helpersEmitted = false; - const helpers = getEmitHelpers(node); - if (helpers) { - for (const helper of stableSort(helpers, compareEmitHelpers)) { - if (!helper.scoped) { - // Skip the helper if it can be skipped and the noEmitHelpers compiler - // option is set, or if it can be imported and the importHelpers compiler - // option is set. - if (shouldSkip) continue; - - // Skip the helper if it can be bundled but hasn't already been emitted and we - // are emitting a bundled module. - if (shouldBundle) { - if (bundledHelpers.get(helper.name)) { - continue; - } - - bundledHelpers.set(helper.name, true); - } - } - else if (isBundle) { - // Skip the helper if it is scoped and we are emitting bundled helpers - continue; - } - - writeLines(helper.text); - helpersEmitted = true; - } - } - - if (helpersEmitted) { - writeLine(); - } - - return helpersEmitted; - } - - function writeLines(text: string): void { - const lines = text.split(/\r\n?|\n/g); - const indentation = guessIndentation(lines); - for (let i = 0; i < lines.length; i++) { - const line = indentation ? lines[i].slice(indentation) : lines[i]; - if (line.length) { - if (i > 0) { - writeLine(); - } - write(line); - } - } - } - - function guessIndentation(lines: string[]) { - let indentation: number; - for (const line of lines) { - for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { - if (!isWhiteSpace(line.charCodeAt(i))) { - if (indentation === undefined || i < indentation) { - indentation = i; - break; - } - } - } - } - return indentation; - } - // // Helpers // function emitShebang() { - const shebang = getShebang(currentText); + const shebang = getShebang(currentSourceFile.text); if (shebang) { write(shebang); writeLine(); @@ -2261,15 +2247,17 @@ namespace ts { } } + // Emit this child. if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); + if (emitTrailingCommentsOfPosition) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos); + } } else { shouldEmitInterveningComments = mayEmitInterveningComments; } - // Emit this child. emit(child); if (shouldDecreaseIndentAfterEmit) { @@ -2305,6 +2293,46 @@ namespace ts { } } + function write(s: string) { + writer.write(s); + } + + function writeLine() { + writer.writeLine(); + } + + function increaseIndent() { + writer.increaseIndent(); + } + + function decreaseIndent() { + writer.decreaseIndent(); + } + + function writeIfAny(nodes: NodeArray, text: string) { + if (some(nodes)) { + write(text); + } + } + + function writeIfPresent(node: Node, text: string) { + if (node) { + write(text); + } + } + + function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { + return onEmitSourceMapOfToken + ? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText) + : writeTokenText(token, pos); + } + + function writeTokenText(token: SyntaxKind, pos?: number) { + const tokenString = tokenToString(token); + write(tokenString); + return pos < 0 ? pos : pos + tokenString.length; + } + function writeLineOrSpace(node: Node) { if (getEmitFlags(node) & EmitFlags.SingleLine) { write(" "); @@ -2314,26 +2342,32 @@ namespace ts { } } - function writeIfAny(nodes: NodeArray, text: string) { - if (nodes && nodes.length > 0) { - write(text); + function writeLines(text: string): void { + const lines = text.split(/\r\n?|\n/g); + const indentation = guessIndentation(lines); + for (let i = 0; i < lines.length; i++) { + const line = indentation ? lines[i].slice(indentation) : lines[i]; + if (line.length) { + writeLine(); + write(line); + writeLine(); + } } } - function writeIfPresent(node: Node, text: string) { - if (node !== undefined) { - write(text); + function guessIndentation(lines: string[]) { + let indentation: number; + for (const line of lines) { + for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { + if (!isWhiteSpace(line.charCodeAt(i))) { + if (indentation === undefined || i < indentation) { + indentation = i; + break; + } + } + } } - } - - function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { - return emitTokenWithSourceMap(contextNode, token, pos, writeTokenText); - } - - function writeTokenText(token: SyntaxKind, pos?: number) { - const tokenString = tokenToString(token); - write(tokenString); - return pos < 0 ? pos : pos + tokenString.length; + return indentation; } function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { @@ -2459,6 +2493,16 @@ namespace ts { && !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile); } + function isSingleLineEmptyBlock(block: Block) { + return !block.multiLine + && isEmptyBlock(block); + } + + function isEmptyBlock(block: BlockLike) { + return block.statements.length === 0 + && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile); + } + function skipSynthesizedParentheses(node: Node) { while (node.kind === SyntaxKind.ParenthesizedExpression && nodeIsSynthesized(node)) { node = (node).expression; @@ -2469,7 +2513,7 @@ namespace ts { function getTextOfNode(node: Node, includeTrivia?: boolean): string { if (isGeneratedIdentifier(node)) { - return getGeneratedIdentifier(node); + return generateName(node); } else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { return unescapeIdentifier(node.text); @@ -2498,22 +2542,57 @@ namespace ts { return getLiteralText(node, currentSourceFile, languageVersion); } - function isSingleLineEmptyBlock(block: Block) { - return !block.multiLine - && isEmptyBlock(block); + /** + * Push a new name generation scope. + */ + function pushNameGenerationScope() { + tempFlagsStack.push(tempFlags); + tempFlags = 0; } - function isEmptyBlock(block: BlockLike) { - return block.statements.length === 0 - && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile); + /** + * Pop the current name generation scope. + */ + function popNameGenerationScope() { + tempFlags = tempFlagsStack.pop(); } + /** + * Generate the text for a generated identifier. + */ + function generateName(name: GeneratedIdentifier) { + if (name.autoGenerateKind === GeneratedIdentifierKind.Node) { + // Node names generate unique names based on their original node + // and are cached based on that node's id. + const node = getNodeForGeneratedName(name); + return generateNameCached(node, getTextOfNode); + } + else { + // Auto, Loop, and Unique names are cached based on their unique + // autoGenerateId. + const autoGenerateId = name.autoGenerateId; + return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(makeName(name))); + } + } + + function generateNameCached(node: Node, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { + const nodeId = getNodeId(node); + return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node, getTextOfNode))); + } + + /** + * Returns a value indicating whether a name is unique globally, within the current file, + * or within the NameGenerator. + */ function isUniqueName(name: string): boolean { - return !resolver.hasGlobalName(name) && - !currentFileIdentifiers.has(name) && - !generatedNameSet.has(name); + return !(hasGlobalName && hasGlobalName(name)) + && !currentSourceFile.identifiers.has(name) + && !generatedNames.has(name); } + /** + * Returns a value indicating whether a name is unique within a container. + */ function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { if (node.locals) { @@ -2528,10 +2607,10 @@ namespace ts { } /** - * Return the next available name in the pattern _a ... _z, _0, _1, ... - * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name. - * Note that names generated by makeTempVariableName and makeUniqueName will never conflict. - */ + * Return the next available name in the pattern _a ... _z, _0, _1, ... + * TempFlags._i or TempFlags._n may be used to express a preference for that dedicated name. + * Note that names generated by makeTempVariableName and makeUniqueName will never conflict. + */ function makeTempVariableName(flags: TempFlags): string { if (flags && !(tempFlags & flags)) { const name = flags === TempFlags._i ? "_i" : "_n"; @@ -2555,10 +2634,12 @@ namespace ts { } } - // Generate a name that is unique within the current file and doesn't conflict with any names - // in global scope. The name is formed by adding an '_n' suffix to the specified base name, - // where n is a positive integer. Note that names generated by makeTempVariableName and - // makeUniqueName are guaranteed to never conflict. + /** + * Generate a name that is unique within the current file and doesn't conflict with any names + * in global scope. The name is formed by adding an '_n' suffix to the specified base name, + * where n is a positive integer. Note that names generated by makeTempVariableName and + * makeUniqueName are guaranteed to never conflict. + */ function makeUniqueName(baseName: string): string { // Find the first unique 'name_n', where n is a positive number if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { @@ -2568,19 +2649,25 @@ namespace ts { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - generatedNameSet.set(generatedName, generatedName); + generatedNames.set(generatedName, generatedName); return generatedName; } i++; } } - function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { + /** + * Generates a unique name for a ModuleDeclaration or EnumDeclaration. + */ + function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { const name = getTextOfNode(node.name); // Use module/enum name itself if it is unique, otherwise make a unique variation return isUniqueLocalName(name, node) ? name : makeUniqueName(name); } + /** + * Generates a unique name for an ImportDeclaration or ExportDeclaration. + */ function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { const expr = getExternalModuleName(node); const baseName = expr.kind === SyntaxKind.StringLiteral ? @@ -2588,33 +2675,37 @@ namespace ts { return makeUniqueName(baseName); } + /** + * Generates a unique name for a default export. + */ function generateNameForExportDefault() { return makeUniqueName("default"); } + /** + * Generates a unique name for a class expression. + */ function generateNameForClassExpression() { return makeUniqueName("class"); } - function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration) { + function generateNameForMethodOrAccessor(node: MethodDeclaration | AccessorDeclaration, getTextOfNode: (node: Node, includeTrivia?: boolean) => string) { if (isIdentifier(node.name)) { - return generateNameForNodeCached(node.name); + return generateNameCached(node.name, getTextOfNode); } return makeTempVariableName(TempFlags.Auto); } /** * Generates a unique name from a node. - * - * @param node A node. */ - function generateNameForNode(node: Node): string { + function generateNameForNode(node: Node, getTextOfNode: (node: Node, includeTrivia?: boolean) => string): string { switch (node.kind) { case SyntaxKind.Identifier: return makeUniqueName(getTextOfNode(node)); case SyntaxKind.ModuleDeclaration: case SyntaxKind.EnumDeclaration: - return generateNameForModuleOrEnum(node); + return generateNameForModuleOrEnum(node, getTextOfNode); case SyntaxKind.ImportDeclaration: case SyntaxKind.ExportDeclaration: return generateNameForImportOrExportDeclaration(node); @@ -2627,7 +2718,7 @@ namespace ts { case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - return generateNameForMethodOrAccessor(node); + return generateNameForMethodOrAccessor(node, getTextOfNode); default: return makeTempVariableName(TempFlags.Auto); } @@ -2635,10 +2726,8 @@ namespace ts { /** * Generates a unique identifier for a node. - * - * @param name A generated name. */ - function generateName(name: Identifier) { + function makeName(name: GeneratedIdentifier) { switch (name.autoGenerateKind) { case GeneratedIdentifierKind.Auto: return makeTempVariableName(TempFlags.Auto); @@ -2653,10 +2742,8 @@ namespace ts { /** * Gets the node from which a name should be generated. - * - * @param name A generated name wrapper. */ - function getNodeForGeneratedName(name: Identifier) { + function getNodeForGeneratedName(name: GeneratedIdentifier) { const autoGenerateId = name.autoGenerateId; let node = name as Node; let original = node.original; @@ -2677,61 +2764,43 @@ namespace ts { // otherwise, return the original node for the source; return node; } + } - function generateNameForNodeCached(node: Node) { - const nodeId = getNodeId(node); - return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = unescapeIdentifier(generateNameForNode(node))); - } + function createDelimiterMap() { + const delimiters: string[] = []; + delimiters[ListFormat.None] = ""; + delimiters[ListFormat.CommaDelimited] = ","; + delimiters[ListFormat.BarDelimited] = " |"; + delimiters[ListFormat.AmpersandDelimited] = " &"; + return delimiters; + } - /** - * Gets the generated identifier text from a generated identifier. - * - * @param name The generated identifier. - */ - function getGeneratedIdentifier(name: Identifier) { - if (name.autoGenerateKind === GeneratedIdentifierKind.Node) { - // Generated names generate unique names based on their original node - // and are cached based on that node's id - const node = getNodeForGeneratedName(name); - return generateNameForNodeCached(node); - } - else { - // Auto, Loop, and Unique names are cached based on their unique - // autoGenerateId. - const autoGenerateId = name.autoGenerateId; - return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = unescapeIdentifier(generateName(name))); - } - } + function getDelimiter(format: ListFormat) { + return delimiters[format & ListFormat.DelimitersMask]; + } - function createDelimiterMap() { - const delimiters: string[] = []; - delimiters[ListFormat.None] = ""; - delimiters[ListFormat.CommaDelimited] = ","; - delimiters[ListFormat.BarDelimited] = " |"; - delimiters[ListFormat.AmpersandDelimited] = " &"; - return delimiters; - } + function createBracketsMap() { + const brackets: string[][] = []; + brackets[ListFormat.Braces] = ["{", "}"]; + brackets[ListFormat.Parenthesis] = ["(", ")"]; + brackets[ListFormat.AngleBrackets] = ["<", ">"]; + brackets[ListFormat.SquareBrackets] = ["[", "]"]; + return brackets; + } - function getDelimiter(format: ListFormat) { - return delimiters[format & ListFormat.DelimitersMask]; - } + function getOpeningBracket(format: ListFormat) { + return brackets[format & ListFormat.BracketsMask][0]; + } - function createBracketsMap() { - const brackets: string[][] = []; - brackets[ListFormat.Braces] = ["{", "}"]; - brackets[ListFormat.Parenthesis] = ["(", ")"]; - brackets[ListFormat.AngleBrackets] = ["<", ">"]; - brackets[ListFormat.SquareBrackets] = ["[", "]"]; - return brackets; - } + function getClosingBracket(format: ListFormat) { + return brackets[format & ListFormat.BracketsMask][1]; + } - function getOpeningBracket(format: ListFormat) { - return brackets[format & ListFormat.BracketsMask][0]; - } - - function getClosingBracket(format: ListFormat) { - return brackets[format & ListFormat.BracketsMask][1]; - } + // Flags enum to track count of temp variables and a few dedicated names + const enum TempFlags { + Auto = 0x00000000, // No preferred name + CountMask = 0x0FFFFFFF, // Temp variable counter + _i = 0x10000000, // Use/preference flag for '_i' } const enum ListFormat { @@ -2758,7 +2827,7 @@ namespace ts { SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node. // Brackets/Braces - Braces = 1 << 9, // The list is surrounded by "{" and "}". + Braces = 1 << 9, // The list is surrounded by "{" and "}". Parenthesis = 1 << 10, // The list is surrounded by "(" and ")". AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">". SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]". @@ -2809,4 +2878,4 @@ namespace ts { Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | Parenthesis, IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets, } -} +} \ No newline at end of file diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index f63094ae16d..f2a8de3a93b 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1530,6 +1530,19 @@ namespace ts { return node; } + export function createBundle(sourceFiles: SourceFile[]) { + const node = createNode(SyntaxKind.Bundle); + node.sourceFiles = sourceFiles; + return node; + } + + export function updateBundle(node: Bundle, sourceFiles: SourceFile[]) { + if (node.sourceFiles !== sourceFiles) { + return createBundle(sourceFiles); + } + return node; + } + // Compound nodes export function createComma(left: Expression, right: Expression) { diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 650b9b0ef02..d743f488e75 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -8,10 +8,9 @@ namespace ts { * * @param filePath The path to the generated output file. * @param sourceMapFilePath The path to the output source map file. - * @param sourceFiles The input source files for the program. - * @param isBundledEmit A value indicating whether the generated output file is a bundle. + * @param sourceFileOrBundle The input source file or bundle for the program. */ - initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void; + initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle): void; /** * Reset the SourceMapWriter to an empty state. @@ -38,11 +37,11 @@ namespace ts { /** * Emits a node with possible leading and trailing source maps. * - * @param emitContext The current emit context + * @param hint The current emit context * @param node The node to emit. * @param emitCallback The callback used to emit the node. */ - emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; /** * Emits a token of a node node with possible leading and trailing source maps. @@ -115,10 +114,9 @@ namespace ts { * * @param filePath The path to the generated output file. * @param sourceMapFilePath The path to the output source map file. - * @param sourceFiles The input source files for the program. - * @param isBundledEmit A value indicating whether the generated output file is a bundle. + * @param sourceFileOrBundle The input source file or bundle for the program. */ - function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { + function initialize(filePath: string, sourceMapFilePath: string, sourceFileOrBundle: SourceFile | Bundle) { if (disabled) { return; } @@ -161,11 +159,10 @@ namespace ts { if (compilerOptions.mapRoot) { sourceMapDir = normalizeSlashes(compilerOptions.mapRoot); - if (!isBundledEmit) { // emitting single module file - Debug.assert(sourceFiles.length === 1); + if (sourceFileOrBundle.kind === SyntaxKind.SourceFile) { // emitting single module file // For modules or multiple emit files the mapRoot will have directory structure like the sources // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map - sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFiles[0], host, sourceMapDir)); + sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(sourceFileOrBundle, host, sourceMapDir)); } if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) { @@ -311,12 +308,13 @@ namespace ts { /** * Emits a node with possible leading and trailing source maps. * + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emitCallback The callback used to emit the node. */ - function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { if (disabled) { - return emitCallback(emitContext, node); + return emitCallback(hint, node); } if (node) { @@ -332,11 +330,11 @@ namespace ts { if (emitFlags & EmitFlags.NoNestedSourceMaps) { disabled = true; - emitCallback(emitContext, node); + emitCallback(hint, node); disabled = false; } else { - emitCallback(emitContext, node); + emitCallback(hint, node); } if (node.kind !== SyntaxKind.NotEmittedStatement diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index cef8c701455..62469df79c1 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -105,14 +105,16 @@ namespace ts { hoistFunctionDeclaration, requestEmitHelper, readEmitHelpers, - onSubstituteNode: (_emitContext, node) => node, + onSubstituteNode: (_, node) => node, enableSubstitution, isSubstitutionEnabled, - onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext), + onEmitNode: (hint, node, callback) => callback(hint, node), enableEmitNotification, isEmitNotificationEnabled }; + performance.mark("beforeTransform"); + // Chain together and initialize each transformer. const transformation = chain(...transformers)(context); @@ -122,6 +124,9 @@ namespace ts { // Disable modification of the lexical environment. lexicalEnvironmentDisabled = true; + performance.mark("afterTransform"); + performance.measure("transformTime", "beforeTransform", "afterTransform"); + return { transformed, emitNodeWithSubstitution, @@ -159,21 +164,16 @@ namespace ts { /** * Emits a node with possible substitution. * - * @param emitContext The current emit context. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emitCallback The callback used to emit the node or its substitute. */ - function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { if (node) { if (isSubstitutionEnabled(node)) { - const substitute = context.onSubstituteNode(emitContext, node); - if (substitute && substitute !== node) { - emitCallback(emitContext, substitute); - return; - } + node = context.onSubstituteNode(hint, node) || node; } - - emitCallback(emitContext, node); + emitCallback(hint, node); } } @@ -196,17 +196,17 @@ namespace ts { /** * Emits a node with possible emit notification. * - * @param emitContext The current emit context. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emitCallback The callback used to emit the node. */ - function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { if (node) { if (isEmitNotificationEnabled(node)) { - context.onEmitNode(emitContext, node, emitCallback); + context.onEmitNode(hint, node, emitCallback); } else { - emitCallback(emitContext, node); + emitCallback(hint, node); } } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 3b8b4524aab..10ebe648fe2 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3435,9 +3435,11 @@ namespace ts { /** * Called by the printer just before a node is printed. * + * @param hint A hint as to the intended usage of the node. * @param node The node to be printed. + * @param emitCallback The callback used to emit the node. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) { // If we are tracking a captured `this`, keep track of the enclosing function. const ancestorFacts = enterSubtree( @@ -3445,11 +3447,11 @@ namespace ts { getEmitFlags(node) & EmitFlags.CapturesThis ? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis : HierarchyFacts.FunctionIncludes); - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); return; } - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } /** @@ -3484,13 +3486,13 @@ namespace ts { /** * Hooks node substitutions. * - * @param emitContext The context for the emitter. + * @param hint The context for the emitter. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); - if (emitContext === EmitContext.Expression) { + if (hint === EmitHint.Expression) { return substituteExpression(node); } diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index c87dbe32518..a7f7b0da0ea 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -396,33 +396,33 @@ namespace ts { /** * Hook for node emit. * + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emit A callback used to emit the node in the printer. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { // If we need to support substitutions for `super` in an async method, // we should track it here. if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { const savedCurrentSuperContainer = currentSuperContainer; currentSuperContainer = node; - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); currentSuperContainer = savedCurrentSuperContainer; } else { - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } } /** * Hooks node substitutions. * + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. - * @param isExpression A value indicating whether the node is to be used in an expression - * position. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); - if (emitContext === EmitContext.Expression) { + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); + if (hint === EmitHint.Expression) { return substituteExpression(node); } diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index c3b011fe36d..8b030642a89 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -12,7 +12,7 @@ namespace ts { const compilerOptions = context.getCompilerOptions(); // enable emit notification only if using --jsx preserve or react-native - let previousOnEmitNode: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; + let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; let noSubstitution: boolean[]; if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) { previousOnEmitNode = context.onEmitNode; @@ -41,9 +41,11 @@ namespace ts { /** * Called by the printer just before a node is printed. * - * @param node The node to be printed. + * @param hint A hint as to the intended usage of the node. + * @param node The node to emit. + * @param emitCallback A callback used to emit the node. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) { switch (node.kind) { case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxClosingElement: @@ -53,21 +55,21 @@ namespace ts { break; } - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } /** * Hooks node substitutions. * - * @param emitContext The context for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { + function onSubstituteNode(hint: EmitHint, node: Node) { if (node.id && noSubstitution && noSubstitution[node.id]) { - return previousOnSubstituteNode(emitContext, node); + return previousOnSubstituteNode(hint, node); } - node = previousOnSubstituteNode(emitContext, node); + node = previousOnSubstituteNode(hint, node); if (isPropertyAccessExpression(node)) { return substitutePropertyAccessExpression(node); } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7dcfe76a427..ee17bdacbaf 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1909,9 +1909,9 @@ namespace ts { return -1; } - function onSubstituteNode(emitContext: EmitContext, node: Node): Node { - node = previousOnSubstituteNode(emitContext, node); - if (emitContext === EmitContext.Expression) { + function onSubstituteNode(hint: EmitHint, node: Node): Node { + node = previousOnSubstituteNode(hint, node); + if (hint === EmitHint.Expression) { return substituteExpression(node); } return node; diff --git a/src/compiler/transformers/module/es2015.ts b/src/compiler/transformers/module/es2015.ts index 5611f890164..86fa1295f7b 100644 --- a/src/compiler/transformers/module/es2015.ts +++ b/src/compiler/transformers/module/es2015.ts @@ -71,18 +71,18 @@ namespace ts { /** * Hook for node emit. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emit A callback used to emit the node in the printer. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (isSourceFile(node)) { currentSourceFile = node; - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); currentSourceFile = undefined; } else { - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } } @@ -93,12 +93,12 @@ namespace ts { /** * Hooks node substitutions. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); - if (isIdentifier(node) && emitContext === EmitContext.Expression) { + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); + if (isIdentifier(node) && hint === EmitHint.Expression) { return substituteExpressionIdentifier(node); } return node; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 0adea4fd540..e072429292a 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1207,24 +1207,24 @@ namespace ts { /** * Hook for node emit notifications. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emit A callback used to emit the node in the printer. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node; currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; noSubstitution = []; - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); currentSourceFile = undefined; currentModuleInfo = undefined; noSubstitution = undefined; } else { - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } } @@ -1235,16 +1235,16 @@ namespace ts { /** * Hooks node substitutions. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); if (node.id && noSubstitution[node.id]) { return node; } - if (emitContext === EmitContext.Expression) { + if (hint === EmitHint.Expression) { return substituteExpression(node); } else if (isShorthandPropertyAssignment(node)) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 3f1488b649d..6c59ea194cb 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1548,11 +1548,11 @@ namespace ts { /** * Hook for node emit notifications. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. - * @param emit A callback used to emit the node in the printer. + * @param emitCallback A callback used to emit the node in the printer. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { const id = getOriginalNodeId(node); currentSourceFile = node; @@ -1564,7 +1564,7 @@ namespace ts { delete noSubstitutionMap[id]; } - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); currentSourceFile = undefined; moduleInfo = undefined; @@ -1572,7 +1572,7 @@ namespace ts { noSubstitution = undefined; } else { - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); } } @@ -1583,16 +1583,16 @@ namespace ts { /** * Hooks node substitutions. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); if (isSubstitutionPrevented(node)) { return node; } - if (emitContext === EmitContext.Expression) { + if (hint === EmitHint.Expression) { return substituteExpression(node); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index e2b8b8fe2ec..a61998e8af9 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -3154,11 +3154,11 @@ namespace ts { /** * Hook for node emit. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emit A callback used to emit the node in the printer. */ - function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { + function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { const savedApplicableSubstitutions = applicableSubstitutions; if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) { @@ -3169,7 +3169,7 @@ namespace ts { applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; } - previousOnEmitNode(emitContext, node, emitCallback); + previousOnEmitNode(hint, node, emitCallback); applicableSubstitutions = savedApplicableSubstitutions; } @@ -3177,12 +3177,12 @@ namespace ts { /** * Hooks node substitutions. * - * @param emitContext A context hint for the emitter. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. */ - function onSubstituteNode(emitContext: EmitContext, node: Node) { - node = previousOnSubstituteNode(emitContext, node); - if (emitContext === EmitContext.Expression) { + function onSubstituteNode(hint: EmitHint, node: Node) { + node = previousOnSubstituteNode(hint, node); + if (hint === EmitHint.Expression) { return substituteExpression(node); } else if (isShorthandPropertyAssignment(node)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 754393b66b0..b416f26324f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -348,6 +348,7 @@ EnumMember, // Top-level nodes SourceFile, + Bundle, // JSDoc nodes JSDocTypeExpression, @@ -2202,6 +2203,11 @@ /* @internal */ ambientModuleNames: string[]; } + export interface Bundle extends Node { + kind: SyntaxKind.Bundle; + sourceFiles: SourceFile[]; + } + export interface ScriptReferenceHost { getCompilerOptions(): CompilerOptions; getSourceFile(fileName: string): SourceFile; @@ -3784,8 +3790,7 @@ LastEmitHelper = Generator } - /* @internal */ - export const enum EmitContext { + export const enum EmitHint { SourceFile, // Emitting a SourceFile Expression, // Emitting an Expression IdentifierName, // Emitting an IdentifierName @@ -3860,7 +3865,7 @@ * Hook used by transformers to substitute expressions just before they * are emitted by the pretty printer. */ - onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; + onSubstituteNode?: (hint: EmitHint, node: Node) => Node; /** * Enables before/after emit notifications in the pretty printer for the provided @@ -3878,7 +3883,7 @@ * Hook used to allow transformers to capture state before or after * the printer emits a node. */ - onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; + onEmitNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; } /* @internal */ @@ -3891,25 +3896,132 @@ /** * Emits the substitute for a node, if one is available; otherwise, emits the node. * - * @param emitContext The current emit context. + * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. * @param emitCallback A callback used to emit the node or its substitute. */ - emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; /** * Emits a node with possible notification. * - * @param emitContext The current emit context. + * @param hint A hint as to the intended usage of the node. * @param node The node to emit. * @param emitCallback A callback used to emit the node. */ - emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; + emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; } /* @internal */ export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; + export interface Printer { + /** + * Print a node and its subtree as-is, without any emit transformations. + * @param hint A value indicating the purpose of a node. This is primarily used to + * distinguish between an `Identifier` used in an expression position, versus an + * `Identifier` used as an `IdentifierName` as part of a declaration. For most nodes you + * should just pass `Unspecified`. + * @param node The node to print. The node and its subtree are printed as-is, without any + * emit transformations. + * @param sourceFile A source file that provides context for the node. The source text of + * the file is used to emit the original source content for literals and identifiers, while + * the identifiers of the source file are used when generating unique names to avoid + * collisions. + */ + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; + /** + * Prints a source file as-is, without any emit transformations. + */ + printFile(sourceFile: SourceFile): string; + /** + * Prints a bundle of source files as-is, without any emit transformations. + */ + printBundle(bundle: Bundle): string; + /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void; + /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; + /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void; + } + + export interface PrintHandlers { + /** + * A hook used by the Printer when generating unique names to avoid collisions with + * globally defined names that exist outside of the current source file. + */ + hasGlobalName?(name: string): boolean; + /** + * A hook used by the Printer to provide notifications prior to emitting a node. A + * compatible implementation **must** invoke `emitCallback` with the provided `hint` and + * `node` values. + * @param hint A hint indicating the intended purpose of the node. + * @param node The node to emit. + * @param emitCallback A callback that, when invoked, will emit the node. + * @example + * ```ts + * var printer = createPrinter(printerOptions, { + * onEmitNode(hint, node, emitCallback) { + * // set up or track state prior to emitting the node... + * emitCallback(hint, node); + * // restore state after emitting the node... + * } + * }); + * ``` + */ + onEmitNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; + /** + * A hook used by the Printer to perform just-in-time substitution of a node. This is + * primarily used by node transformations that need to substitute one node for another, + * such as replacing `myExportedVar` with `exports.myExportedVar`. A compatible + * implementation **must** invoke `emitCallback` eith the provided `hint` and either + * the provided `node`, or its substitute. + * @param hint A hint indicating the intended purpose of the node. + * @param node The node to emit. + * @param emitCallback A callback that, when invoked, will emit the node. + * @example + * ```ts + * var printer = createPrinter(printerOptions, { + * onSubstituteNode(hint, node, emitCallback) { + * // perform substitution if necessary... + * emitCallback(hint, node); + * } + * }); + * ``` + */ + onSubstituteNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; + /*@internal*/ onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; + /*@internal*/ onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number; + /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void; + /*@internal*/ onEmitHelpers?: (node: Node, writeLines: (text: string) => void) => void; + /*@internal*/ onSetSourceFile?: (node: SourceFile) => void; + } + + export interface PrinterOptions { + target?: ScriptTarget; + removeComments?: boolean; + newLine?: NewLineKind; + /*@internal*/ sourceMap?: boolean; + /*@internal*/ inlineSourceMap?: boolean; + /*@internal*/ extendedDiagnostics?: boolean; + } + + /*@internal*/ + export interface EmitTextWriter { + write(s: string): void; + writeTextOfNode(text: string, node: Node): void; + writeLine(): void; + increaseIndent(): void; + decreaseIndent(): void; + getText(): string; + rawWrite(s: string): void; + writeLiteral(s: string): void; + getTextPos(): number; + getLine(): number; + getColumn(): number; + getIndent(): number; + isAtStartOfLine(): boolean; + reset(): void; + } + export interface TextSpan { start: number; length: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 70bb652f9ca..cbfb93d4f14 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2107,16 +2107,19 @@ namespace ts { return undefined; } - export function getOriginalSourceFiles(sourceFiles: SourceFile[]) { - const originalSourceFiles: SourceFile[] = []; - for (const sourceFile of sourceFiles) { - const originalSourceFile = getParseTreeNode(sourceFile, isSourceFile); - if (originalSourceFile) { - originalSourceFiles.push(originalSourceFile); - } + export function getOriginalSourceFileOrBundle(sourceFileOrBundle: SourceFile | Bundle) { + if (sourceFileOrBundle.kind === SyntaxKind.Bundle) { + return updateBundle(sourceFileOrBundle, sameMap(sourceFileOrBundle.sourceFiles, getOriginalSourceFile)); } + return getOriginalSourceFile(sourceFileOrBundle); + } - return originalSourceFiles; + function getOriginalSourceFile(sourceFile: SourceFile) { + return getParseTreeNode(sourceFile, isSourceFile) || sourceFile; + } + + export function getOriginalSourceFiles(sourceFiles: SourceFile[]) { + return sameMap(sourceFiles, getOriginalSourceFile); } export function getOriginalNodeId(node: Node) { @@ -2455,23 +2458,6 @@ namespace ts { s; } - export interface EmitTextWriter { - write(s: string): void; - writeTextOfNode(text: string, node: Node): void; - writeLine(): void; - increaseIndent(): void; - decreaseIndent(): void; - getText(): string; - rawWrite(s: string): void; - writeLiteral(s: string): void; - getTextPos(): number; - getLine(): number; - getColumn(): number; - getIndent(): number; - isAtStartOfLine(): boolean; - reset(): void; - } - const indentStrings: string[] = ["", " "]; export function getIndentString(level: number) { if (indentStrings[level] === undefined) { @@ -2654,7 +2640,7 @@ namespace ts { * Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit. */ export function forEachEmittedFile( - host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void, + host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle, emitOnlyDtsFiles: boolean) => void, sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile, emitOnlyDtsFiles?: boolean) { @@ -2665,7 +2651,7 @@ namespace ts { const jsFilePath = options.outFile || options.out; const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined; - action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFiles, /*isBundledEmit*/true, emitOnlyDtsFiles); + action({ jsFilePath, sourceMapFilePath, declarationFilePath }, createBundle(sourceFiles), emitOnlyDtsFiles); } } else { @@ -2673,7 +2659,7 @@ namespace ts { const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; - action({ jsFilePath, sourceMapFilePath, declarationFilePath }, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles); + action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFile, emitOnlyDtsFiles); } } } @@ -3248,7 +3234,7 @@ namespace ts { const carriageReturnLineFeed = "\r\n"; const lineFeed = "\n"; - export function getNewLineCharacter(options: CompilerOptions): string { + export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string { if (options.newLine === NewLineKind.CarriageReturnLineFeed) { return carriageReturnLineFeed; } diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index ebd07118cc5..bea688d358b 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -118,6 +118,7 @@ "./unittests/initializeTSConfig.ts", "./unittests/compileOnSave.ts", "./unittests/typingsInstaller.ts", - "./unittests/projectErrors.ts" + "./unittests/projectErrors.ts", + "./unittests/printer.ts" ] } diff --git a/src/harness/unittests/printer.ts b/src/harness/unittests/printer.ts new file mode 100644 index 00000000000..2496a880bc5 --- /dev/null +++ b/src/harness/unittests/printer.ts @@ -0,0 +1,97 @@ +/// +/// + +namespace ts { + describe("PrinterAPI", () => { + function makePrintsCorrectly(prefix: string) { + return function printsCorrectly(name: string, options: PrinterOptions, printCallback: (printer: Printer) => string) { + it(name, () => { + Harness.Baseline.runBaseline(`printerApi/${prefix}.${name}.js`, () => + printCallback(createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed, ...options }))); + }); + } + } + + describe("printFile", () => { + const printsCorrectly = makePrintsCorrectly("printsFileCorrectly"); + const sourceFile = createSourceFile("source.ts", ` + interface A { + // comment1 + readonly prop?: T; + + // comment2 + method(): void; + + // comment3 + new (): A; + + // comment4 + (): A; + } + + // comment5 + type B = number | string | object; + type C = A & { x: string; }; // comment6 + + // comment7 + enum E1 { + // comment8 + first + } + + const enum E2 { + second + } + + // comment9 + console.log(1 + 2); + `, ScriptTarget.ES2015); + + printsCorrectly("default", {}, printer => printer.printFile(sourceFile)); + printsCorrectly("removeComments", { removeComments: true }, printer => printer.printFile(sourceFile)); + }); + + describe("printBundle", () => { + const printsCorrectly = makePrintsCorrectly("printsBundleCorrectly"); + const bundle = createBundle([ + createSourceFile("a.ts", ` + /*! [a.ts] */ + + // comment0 + const a = 1; + `, ScriptTarget.ES2015), + createSourceFile("b.ts", ` + /*! [b.ts] */ + + // comment1 + const b = 2; + `, ScriptTarget.ES2015) + ]); + printsCorrectly("default", {}, printer => printer.printBundle(bundle)); + printsCorrectly("removeComments", { removeComments: true }, printer => printer.printBundle(bundle)); + }); + + describe("printNode", () => { + const printsCorrectly = makePrintsCorrectly("printsNodeCorrectly"); + const sourceFile = createSourceFile("source.ts", "", ScriptTarget.ES2015); + const syntheticNode = createClassDeclaration( + undefined, + undefined, + /*name*/ createIdentifier("C"), + undefined, + undefined, + createNodeArray([ + createProperty( + undefined, + createNodeArray([createToken(SyntaxKind.PublicKeyword)]), + createIdentifier("prop"), + undefined, + undefined, + undefined + ) + ]) + ); + printsCorrectly("class", {}, printer => printer.printNode(EmitHint.Unspecified, syntheticNode, sourceFile)); + }); + }); +} diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index d4956a387ee..2b79141c3e3 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -44,6 +44,8 @@ namespace ts.projectSystem { }); } + import typingsName = server.typingsInstaller.typingsName; + describe("typingsInstaller", () => { it("configured projects (typings installed) 1", () => { const file1 = { @@ -519,32 +521,32 @@ namespace ts.projectSystem { const commander = { path: "/a/data/node_modules/@types/commander/index.d.ts", content: "declare const commander: { x: number }", - typings: "@types/commander" + typings: typingsName("commander") }; const jquery = { path: "/a/data/node_modules/@types/jquery/index.d.ts", content: "declare const jquery: { x: number }", - typings: "@types/jquery" + typings: typingsName("jquery") }; const lodash = { path: "/a/data/node_modules/@types/lodash/index.d.ts", content: "declare const lodash: { x: number }", - typings: "@types/lodash" + typings: typingsName("lodash") }; const cordova = { path: "/a/data/node_modules/@types/cordova/index.d.ts", content: "declare const cordova: { x: number }", - typings: "@types/cordova" + typings: typingsName("cordova") }; const grunt = { path: "/a/data/node_modules/@types/grunt/index.d.ts", content: "declare const grunt: { x: number }", - typings: "@types/grunt" + typings: typingsName("grunt") }; const gulp = { path: "/a/data/node_modules/@types/gulp/index.d.ts", content: "declare const gulp: { x: number }", - typings: "@types/gulp" + typings: typingsName("gulp") }; const host = createServerHost([lodashJs, commanderJs, file3]); @@ -554,7 +556,7 @@ namespace ts.projectSystem { } installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void { let typingFiles: (FileOrFolder & { typings: string })[] = []; - if (args.indexOf("@types/commander") >= 0) { + if (args.indexOf(typingsName("commander")) >= 0) { typingFiles = [commander, jquery, lodash, cordova]; } else { @@ -982,7 +984,7 @@ namespace ts.projectSystem { return; } if (response.kind === server.EventEndInstallTypes) { - assert.deepEqual(response.packagesToInstall, ["@types/commander"]); + assert.deepEqual(response.packagesToInstall, [typingsName("commander")]); seenTelemetryEvent = true; return; } diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 1869c542a6e..e889f98789a 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -295,8 +295,7 @@ namespace ts.server.typingsInstaller { this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`); } const filteredTypings = this.filterTypings(typingsToInstall); - const scopedTypings = filteredTypings.map(x => `@types/${x}`); - if (scopedTypings.length === 0) { + if (filteredTypings.length === 0) { if (this.log.isEnabled()) { this.log.writeLine(`All typings are known to be missing or invalid - no need to go any further`); } @@ -316,6 +315,7 @@ namespace ts.server.typingsInstaller { projectName: req.projectName }); + const scopedTypings = filteredTypings.map(typingsName); this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => { try { if (!ok) { @@ -429,4 +429,10 @@ namespace ts.server.typingsInstaller { protected abstract installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void; protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void; } + + /* @internal */ + export function typingsName(packageName: string): string { + return `@types/${packageName}@ts${versionMajorMinor}`; + } + const versionMajorMinor = version.split(".").slice(0, 2).join("."); } \ No newline at end of file diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 49b8526a1e0..1f1b499d03b 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -115,6 +115,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes: [ Diagnostics.Cannot_find_name_0.code, + Diagnostics.Cannot_find_namespace_0.code, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code ], getCodeActions: (context: CodeFixContext) => { @@ -416,8 +417,8 @@ namespace ts.codefix { ); function getModuleSpecifierForNewImport() { - const fileName = sourceFile.path; - const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().path; + const fileName = sourceFile.fileName; + const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName; const sourceDirectory = getDirectoryPath(fileName); const options = context.program.getCompilerOptions(); @@ -439,8 +440,7 @@ namespace ts.codefix { return undefined; } - const normalizedBaseUrl = toPath(options.baseUrl, getDirectoryPath(options.baseUrl), getCanonicalFileName); - let relativeName = tryRemoveParentDirectoryName(moduleFileName, normalizedBaseUrl); + let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl); if (!relativeName) { return undefined; } @@ -477,9 +477,8 @@ namespace ts.codefix { function tryGetModuleNameFromRootDirs() { if (options.rootDirs) { - const normalizedRootDirs = map(options.rootDirs, rootDir => toPath(rootDir, /*basePath*/ undefined, getCanonicalFileName)); - const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, normalizedRootDirs); - const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, normalizedRootDirs); + const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs); + const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs); if (normalizedTargetPath !== undefined) { const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath; return removeFileExtension(relativePath); @@ -546,9 +545,9 @@ namespace ts.codefix { } } - function getPathRelativeToRootDirs(path: Path, rootDirs: Path[]) { + function getPathRelativeToRootDirs(path: string, rootDirs: string[]) { for (const rootDir of rootDirs) { - const relativeName = tryRemoveParentDirectoryName(path, rootDir); + const relativeName = getRelativePathIfInDirectory(path, rootDir); if (relativeName !== undefined) { return relativeName; } @@ -564,20 +563,15 @@ namespace ts.codefix { return fileName; } + function getRelativePathIfInDirectory(path: string, directoryPath: string) { + const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, false); + return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath; + } + function getRelativePath(path: string, directoryPath: string) { const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, false); return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath; } - - function tryRemoveParentDirectoryName(path: Path, parentDirectory: Path) { - const index = path.indexOf(parentDirectory); - if (index === 0) { - return endsWith(parentDirectory, directorySeparator) - ? path.substring(parentDirectory.length) - : path.substring(parentDirectory.length + 1); - } - return undefined; - } } } diff --git a/src/services/codefixes/unusedIdentifierFixes.ts b/src/services/codefixes/unusedIdentifierFixes.ts index 2784a09a504..0ca01a81eb1 100644 --- a/src/services/codefixes/unusedIdentifierFixes.ts +++ b/src/services/codefixes/unusedIdentifierFixes.ts @@ -25,7 +25,7 @@ namespace ts.codefix { const forStatement = token.parent.parent.parent; const forInitializer = forStatement.initializer; if (forInitializer.declarations.length === 1) { - return createCodeFix("", forInitializer.pos, forInitializer.end - forInitializer.pos); + return createCodeFixToRemoveNode(forInitializer); } else { return removeSingleItem(forInitializer.declarations, token); @@ -35,7 +35,7 @@ namespace ts.codefix { const forOfStatement = token.parent.parent.parent; if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) { const forOfInitializer = forOfStatement.initializer; - return createCodeFix("{}", forOfInitializer.declarations[0].pos, forOfInitializer.declarations[0].end - forOfInitializer.declarations[0].pos); + return createCodeFix("{}", forOfInitializer.declarations[0].getStart(), forOfInitializer.declarations[0].getWidth()); } break; @@ -47,12 +47,12 @@ namespace ts.codefix { case SyntaxKind.CatchClause: const catchClause = token.parent.parent; const parameter = catchClause.variableDeclaration.getChildren()[0]; - return createCodeFix("", parameter.pos, parameter.end - parameter.pos); + return createCodeFixToRemoveNode(parameter); default: const variableStatement = token.parent.parent.parent; if (variableStatement.declarationList.declarations.length === 1) { - return createCodeFix("", variableStatement.pos, variableStatement.end - variableStatement.pos); + return createCodeFixToRemoveNode(variableStatement); } else { const declarations = variableStatement.declarationList.declarations; @@ -72,7 +72,7 @@ namespace ts.codefix { case ts.SyntaxKind.Parameter: const functionDeclaration = token.parent.parent; if (functionDeclaration.parameters.length === 1) { - return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); + return createCodeFixToRemoveNode(token.parent); } else { return removeSingleItem(functionDeclaration.parameters, token); @@ -81,14 +81,14 @@ namespace ts.codefix { // handle case where 'import a = A;' case SyntaxKind.ImportEqualsDeclaration: const importEquals = findImportDeclaration(token); - return createCodeFix("", importEquals.pos, importEquals.end - importEquals.pos); + return createCodeFixToRemoveNode(importEquals); case SyntaxKind.ImportSpecifier: const namedImports = token.parent.parent; if (namedImports.elements.length === 1) { // Only 1 import and it is unused. So the entire declaration should be removed. const importSpec = findImportDeclaration(token); - return createCodeFix("", importSpec.pos, importSpec.end - importSpec.pos); + return createCodeFixToRemoveNode(importSpec); } else { return removeSingleItem(namedImports.elements, token); @@ -100,17 +100,24 @@ namespace ts.codefix { const importClause = token.parent; if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'| const importDecl = findImportDeclaration(importClause); - return createCodeFix("", importDecl.pos, importDecl.end - importDecl.pos); + return createCodeFixToRemoveNode(importDecl); } - else { // import |d,| * as ns from './file' - return createCodeFix("", importClause.name.pos, importClause.namedBindings.pos - importClause.name.pos); + else { + // import |d,| * as ns from './file' + const start = importClause.name.getStart(); + let end = findFirstNonSpaceCharPosStarting(importClause.name.end); + if (sourceFile.text.charCodeAt(end) === CharacterCodes.comma) { + end = findFirstNonSpaceCharPosStarting(end + 1); + } + + return createCodeFix("", start, end - start); } case SyntaxKind.NamespaceImport: const namespaceImport = token.parent; if (namespaceImport.name == token && !(namespaceImport.parent).name) { const importDecl = findImportDeclaration(namespaceImport); - return createCodeFix("", importDecl.pos, importDecl.end - importDecl.pos); + return createCodeFixToRemoveNode(importDecl); } else { const start = (namespaceImport.parent).name.end; @@ -120,16 +127,14 @@ namespace ts.codefix { break; case SyntaxKind.PropertyDeclaration: - return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); - case SyntaxKind.NamespaceImport: - return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); + return createCodeFixToRemoveNode(token.parent); } if (isDeclarationName(token)) { - return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); + return createCodeFixToRemoveNode(token.parent); } else if (isLiteralComputedPropertyDeclarationName(token)) { - return createCodeFix("", token.parent.parent.pos, token.parent.parent.end - token.parent.parent.pos); + return createCodeFixToRemoveNode(token.parent.parent); } else { return undefined; @@ -144,6 +149,17 @@ namespace ts.codefix { return importDecl; } + function createCodeFixToRemoveNode(node: Node) { + return createCodeFix("", node.getStart(), node.getWidth()); + } + + function findFirstNonSpaceCharPosStarting(start: number) { + while (isWhiteSpace(sourceFile.text.charCodeAt(start))) { + start += 1; + } + return start; + } + function createCodeFix(newText: string, start: number, length: number): CodeAction[] { return [{ description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), { 0: token.getText() }), diff --git a/src/services/completions.ts b/src/services/completions.ts index 5a08dbcb9c8..df78ab931d1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -173,6 +173,18 @@ namespace ts.Completions { // var y = require("/*completion position*/"); return getStringLiteralCompletionEntriesFromModuleNames(node, compilerOptions, host, typeChecker); } + else if (isEqualityExpression(node.parent)) { + // Get completions from the type of the other operand + // i.e. switch (a) { + // case '/*completion position*/' + // } + return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation(node.parent.left === node ? node.parent.right : node.parent.left), typeChecker); + } + else if (isCaseOrDefaultClause(node.parent)) { + // Get completions from the type of the switch expression + // i.e. x === '/*completion position' + return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation((node.parent.parent.parent).expression), typeChecker); + } else { const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { @@ -184,7 +196,7 @@ namespace ts.Completions { // Get completion for string literal from string literal type // i.e. var x: "hi" | "hello" = "/*completion position*/" - return getStringLiteralCompletionEntriesFromContextualType(node, typeChecker); + return getStringLiteralCompletionEntriesFromType(typeChecker.getContextualType(node), typeChecker); } } @@ -228,8 +240,7 @@ namespace ts.Completions { return undefined; } - function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral, typeChecker: TypeChecker): CompletionInfo | undefined { - const type = typeChecker.getContextualType(node); + function getStringLiteralCompletionEntriesFromType(type: Type, typeChecker: TypeChecker): CompletionInfo | undefined { if (type) { const entries: CompletionEntry[] = []; addStringLiteralCompletionsFromType(type, entries, typeChecker); @@ -1756,4 +1767,15 @@ namespace ts.Completions { catch (e) {} return undefined; } + + function isEqualityExpression(node: Node): node is BinaryExpression { + return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind); + } + + function isEqualityOperatorKind(kind: SyntaxKind) { + return kind == SyntaxKind.EqualsEqualsToken || + kind === SyntaxKind.ExclamationEqualsToken || + kind === SyntaxKind.EqualsEqualsEqualsToken || + kind === SyntaxKind.ExclamationEqualsEqualsToken; + } } diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index e7df8bbd5a1..2b1b22d3595 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -516,6 +516,43 @@ class B extends A<{ x: number}> { p.x; } } + +// Repro from #13749 + +class Form { + private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} + + public set(prop: K, value: T[K]) { + this.childFormFactories[prop](value) + } +} + +// Repro from #13787 + +class SampleClass

{ + public props: Readonly

; + constructor(props: P) { + this.props = Object.freeze(props); + } +} + +interface Foo { + foo: string; +} + +declare function merge(obj1: T, obj2: U): T & U; + +class AnotherSampleClass extends SampleClass { + constructor(props: T) { + const foo: Foo = { foo: "bar" }; + super(merge(props, foo)); + } + + public brokenMethod() { + this.props.foo.concat; + } +} +new AnotherSampleClass({}); //// [keyofAndIndexedAccess.js] @@ -862,6 +899,36 @@ var B = (function (_super) { }; return B; }(A)); +// Repro from #13749 +var Form = (function () { + function Form() { + } + Form.prototype.set = function (prop, value) { + this.childFormFactories[prop](value); + }; + return Form; +}()); +// Repro from #13787 +var SampleClass = (function () { + function SampleClass(props) { + this.props = Object.freeze(props); + } + return SampleClass; +}()); +var AnotherSampleClass = (function (_super) { + __extends(AnotherSampleClass, _super); + function AnotherSampleClass(props) { + var _this = this; + var foo = { foo: "bar" }; + _this = _super.call(this, merge(props, foo)) || this; + return _this; + } + AnotherSampleClass.prototype.brokenMethod = function () { + this.props.foo.concat; + }; + return AnotherSampleClass; +}(SampleClass)); +new AnotherSampleClass({}); //// [keyofAndIndexedAccess.d.ts] @@ -1104,3 +1171,19 @@ declare class B extends A<{ }> { f(p: this["props"]): void; } +declare class Form { + private childFormFactories; + set(prop: K, value: T[K]): void; +} +declare class SampleClass

{ + props: Readonly

; + constructor(props: P); +} +interface Foo { + foo: string; +} +declare function merge(obj1: T, obj2: U): T & U; +declare class AnotherSampleClass extends SampleClass { + constructor(props: T); + brokenMethod(): void; +} diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index 01501cd719c..f6425b5ed0e 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -1844,3 +1844,122 @@ class B extends A<{ x: number}> { } } +// Repro from #13749 + +class Form { +>Form : Symbol(Form, Decl(keyofAndIndexedAccess.ts, 516, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) + + private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} +>childFormFactories : Symbol(Form.childFormFactories, Decl(keyofAndIndexedAccess.ts, 520, 15)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 521, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) +>v : Symbol(v, Decl(keyofAndIndexedAccess.ts, 521, 50)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 521, 34)) +>Form : Symbol(Form, Decl(keyofAndIndexedAccess.ts, 516, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 521, 34)) + + public set(prop: K, value: T[K]) { +>set : Symbol(Form.set, Decl(keyofAndIndexedAccess.ts, 521, 73)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 523, 15)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 523, 34)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 523, 15)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 523, 42)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 520, 11)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 523, 15)) + + this.childFormFactories[prop](value) +>this.childFormFactories : Symbol(Form.childFormFactories, Decl(keyofAndIndexedAccess.ts, 520, 15)) +>this : Symbol(Form, Decl(keyofAndIndexedAccess.ts, 516, 1)) +>childFormFactories : Symbol(Form.childFormFactories, Decl(keyofAndIndexedAccess.ts, 520, 15)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 523, 34)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 523, 42)) + } +} + +// Repro from #13787 + +class SampleClass

{ +>SampleClass : Symbol(SampleClass, Decl(keyofAndIndexedAccess.ts, 526, 1)) +>P : Symbol(P, Decl(keyofAndIndexedAccess.ts, 530, 18)) + + public props: Readonly

; +>props : Symbol(SampleClass.props, Decl(keyofAndIndexedAccess.ts, 530, 22)) +>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --)) +>P : Symbol(P, Decl(keyofAndIndexedAccess.ts, 530, 18)) + + constructor(props: P) { +>props : Symbol(props, Decl(keyofAndIndexedAccess.ts, 532, 16)) +>P : Symbol(P, Decl(keyofAndIndexedAccess.ts, 530, 18)) + + this.props = Object.freeze(props); +>this.props : Symbol(SampleClass.props, Decl(keyofAndIndexedAccess.ts, 530, 22)) +>this : Symbol(SampleClass, Decl(keyofAndIndexedAccess.ts, 526, 1)) +>props : Symbol(SampleClass.props, Decl(keyofAndIndexedAccess.ts, 530, 22)) +>Object.freeze : Symbol(ObjectConstructor.freeze, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>freeze : Symbol(ObjectConstructor.freeze, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>props : Symbol(props, Decl(keyofAndIndexedAccess.ts, 532, 16)) + } +} + +interface Foo { +>Foo : Symbol(Foo, Decl(keyofAndIndexedAccess.ts, 535, 1)) + + foo: string; +>foo : Symbol(Foo.foo, Decl(keyofAndIndexedAccess.ts, 537, 15)) +} + +declare function merge(obj1: T, obj2: U): T & U; +>merge : Symbol(merge, Decl(keyofAndIndexedAccess.ts, 539, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 541, 23)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 541, 25)) +>obj1 : Symbol(obj1, Decl(keyofAndIndexedAccess.ts, 541, 29)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 541, 23)) +>obj2 : Symbol(obj2, Decl(keyofAndIndexedAccess.ts, 541, 37)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 541, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 541, 23)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 541, 25)) + +class AnotherSampleClass extends SampleClass { +>AnotherSampleClass : Symbol(AnotherSampleClass, Decl(keyofAndIndexedAccess.ts, 541, 54)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 543, 25)) +>SampleClass : Symbol(SampleClass, Decl(keyofAndIndexedAccess.ts, 526, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 543, 25)) +>Foo : Symbol(Foo, Decl(keyofAndIndexedAccess.ts, 535, 1)) + + constructor(props: T) { +>props : Symbol(props, Decl(keyofAndIndexedAccess.ts, 544, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 543, 25)) + + const foo: Foo = { foo: "bar" }; +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 545, 13)) +>Foo : Symbol(Foo, Decl(keyofAndIndexedAccess.ts, 535, 1)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 545, 26)) + + super(merge(props, foo)); +>super : Symbol(SampleClass, Decl(keyofAndIndexedAccess.ts, 526, 1)) +>merge : Symbol(merge, Decl(keyofAndIndexedAccess.ts, 539, 1)) +>props : Symbol(props, Decl(keyofAndIndexedAccess.ts, 544, 16)) +>foo : Symbol(foo, Decl(keyofAndIndexedAccess.ts, 545, 13)) + } + + public brokenMethod() { +>brokenMethod : Symbol(AnotherSampleClass.brokenMethod, Decl(keyofAndIndexedAccess.ts, 547, 5)) + + this.props.foo.concat; +>this.props.foo.concat : Symbol(String.concat, Decl(lib.d.ts, --, --)) +>this.props.foo : Symbol(foo) +>this.props : Symbol(SampleClass.props, Decl(keyofAndIndexedAccess.ts, 530, 22)) +>this : Symbol(AnotherSampleClass, Decl(keyofAndIndexedAccess.ts, 541, 54)) +>props : Symbol(SampleClass.props, Decl(keyofAndIndexedAccess.ts, 530, 22)) +>foo : Symbol(foo) +>concat : Symbol(String.concat, Decl(lib.d.ts, --, --)) + } +} +new AnotherSampleClass({}); +>AnotherSampleClass : Symbol(AnotherSampleClass, Decl(keyofAndIndexedAccess.ts, 541, 54)) + diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 21c9d645ce4..86469efc284 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -2166,3 +2166,132 @@ class B extends A<{ x: number}> { } } +// Repro from #13749 + +class Form { +>Form : Form +>T : T + + private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} +>childFormFactories : { [K in keyof T]: (v: T[K]) => Form; } +>K : K +>T : T +>v : T[K] +>T : T +>K : K +>Form : Form +>T : T +>K : K + + public set(prop: K, value: T[K]) { +>set : (prop: K, value: T[K]) => void +>K : K +>T : T +>prop : K +>K : K +>value : T[K] +>T : T +>K : K + + this.childFormFactories[prop](value) +>this.childFormFactories[prop](value) : Form +>this.childFormFactories[prop] : (v: T[K]) => Form +>this.childFormFactories : { [K in keyof T]: (v: T[K]) => Form; } +>this : this +>childFormFactories : { [K in keyof T]: (v: T[K]) => Form; } +>prop : K +>value : T[K] + } +} + +// Repro from #13787 + +class SampleClass

{ +>SampleClass : SampleClass

+>P : P + + public props: Readonly

; +>props : Readonly

+>Readonly : Readonly +>P : P + + constructor(props: P) { +>props : P +>P : P + + this.props = Object.freeze(props); +>this.props = Object.freeze(props) : Readonly

+>this.props : Readonly

+>this : this +>props : Readonly

+>Object.freeze(props) : Readonly

+>Object.freeze : { (a: T[]): ReadonlyArray; (f: T): T; (o: T): Readonly; } +>Object : ObjectConstructor +>freeze : { (a: T[]): ReadonlyArray; (f: T): T; (o: T): Readonly; } +>props : P + } +} + +interface Foo { +>Foo : Foo + + foo: string; +>foo : string +} + +declare function merge(obj1: T, obj2: U): T & U; +>merge : (obj1: T, obj2: U) => T & U +>T : T +>U : U +>obj1 : T +>T : T +>obj2 : U +>U : U +>T : T +>U : U + +class AnotherSampleClass extends SampleClass { +>AnotherSampleClass : AnotherSampleClass +>T : T +>SampleClass : SampleClass +>T : T +>Foo : Foo + + constructor(props: T) { +>props : T +>T : T + + const foo: Foo = { foo: "bar" }; +>foo : Foo +>Foo : Foo +>{ foo: "bar" } : { foo: string; } +>foo : string +>"bar" : "bar" + + super(merge(props, foo)); +>super(merge(props, foo)) : void +>super : typeof SampleClass +>merge(props, foo) : T & Foo +>merge : (obj1: T, obj2: U) => T & U +>props : T +>foo : Foo + } + + public brokenMethod() { +>brokenMethod : () => void + + this.props.foo.concat; +>this.props.foo.concat : (...strings: string[]) => string +>this.props.foo : (T & Foo)["foo"] +>this.props : Readonly +>this : this +>props : Readonly +>foo : (T & Foo)["foo"] +>concat : (...strings: string[]) => string + } +} +new AnotherSampleClass({}); +>new AnotherSampleClass({}) : AnotherSampleClass<{}> +>AnotherSampleClass : typeof AnotherSampleClass +>{} : {} + diff --git a/tests/baselines/reference/mappedTypesAndObjects.js b/tests/baselines/reference/mappedTypesAndObjects.js index 53b39209693..f320cc9624e 100644 --- a/tests/baselines/reference/mappedTypesAndObjects.js +++ b/tests/baselines/reference/mappedTypesAndObjects.js @@ -12,26 +12,37 @@ function f2(x: Partial, y: Readonly) { obj = y; } +function f3(x: Partial) { + x = {}; +} + // Repro from #12900 interface Base { - foo: { [key: string]: any }; - bar: any; - baz: any; + foo: { [key: string]: any }; + bar: any; + baz: any; } interface E1 extends Base { - foo: T; + foo: T; } interface Something { name: string, value: string }; interface E2 extends Base { - foo: Partial; // or other mapped type + foo: Partial; // or other mapped type } interface E3 extends Base { - foo: Partial; // or other mapped type -} + foo: Partial; // or other mapped type +} + +// Repro from #13747 + +class Form { + private values: {[P in keyof T]?: T[P]} = {} +} + //// [mappedTypesAndObjects.js] function f1(x, y) { @@ -44,12 +55,23 @@ function f2(x, y) { obj = x; obj = y; } +function f3(x) { + x = {}; +} ; +// Repro from #13747 +var Form = (function () { + function Form() { + this.values = {}; + } + return Form; +}()); //// [mappedTypesAndObjects.d.ts] declare function f1(x: Partial, y: Readonly): void; declare function f2(x: Partial, y: Readonly): void; +declare function f3(x: Partial): void; interface Base { foo: { [key: string]: any; @@ -70,3 +92,6 @@ interface E2 extends Base { interface E3 extends Base { foo: Partial; } +declare class Form { + private values; +} diff --git a/tests/baselines/reference/mappedTypesAndObjects.symbols b/tests/baselines/reference/mappedTypesAndObjects.symbols index 1690f1b6b04..6fb5d720fb3 100644 --- a/tests/baselines/reference/mappedTypesAndObjects.symbols +++ b/tests/baselines/reference/mappedTypesAndObjects.symbols @@ -45,54 +45,80 @@ function f2(x: Partial, y: Readonly) { >y : Symbol(y, Decl(mappedTypesAndObjects.ts, 7, 29)) } +function f3(x: Partial) { +>f3 : Symbol(f3, Decl(mappedTypesAndObjects.ts, 11, 1)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 13, 12)) +>x : Symbol(x, Decl(mappedTypesAndObjects.ts, 13, 15)) +>Partial : Symbol(Partial, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 13, 12)) + + x = {}; +>x : Symbol(x, Decl(mappedTypesAndObjects.ts, 13, 15)) +} + // Repro from #12900 interface Base { ->Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1)) +>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1)) - foo: { [key: string]: any }; ->foo : Symbol(Base.foo, Decl(mappedTypesAndObjects.ts, 15, 16)) ->key : Symbol(key, Decl(mappedTypesAndObjects.ts, 16, 11)) + foo: { [key: string]: any }; +>foo : Symbol(Base.foo, Decl(mappedTypesAndObjects.ts, 19, 16)) +>key : Symbol(key, Decl(mappedTypesAndObjects.ts, 20, 12)) - bar: any; ->bar : Symbol(Base.bar, Decl(mappedTypesAndObjects.ts, 16, 31)) + bar: any; +>bar : Symbol(Base.bar, Decl(mappedTypesAndObjects.ts, 20, 32)) - baz: any; ->baz : Symbol(Base.baz, Decl(mappedTypesAndObjects.ts, 17, 12)) + baz: any; +>baz : Symbol(Base.baz, Decl(mappedTypesAndObjects.ts, 21, 13)) } interface E1 extends Base { ->E1 : Symbol(E1, Decl(mappedTypesAndObjects.ts, 19, 1)) ->T : Symbol(T, Decl(mappedTypesAndObjects.ts, 21, 13)) ->Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1)) +>E1 : Symbol(E1, Decl(mappedTypesAndObjects.ts, 23, 1)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 25, 13)) +>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1)) - foo: T; ->foo : Symbol(E1.foo, Decl(mappedTypesAndObjects.ts, 21, 30)) ->T : Symbol(T, Decl(mappedTypesAndObjects.ts, 21, 13)) + foo: T; +>foo : Symbol(E1.foo, Decl(mappedTypesAndObjects.ts, 25, 30)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 25, 13)) } interface Something { name: string, value: string }; ->Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 23, 1)) ->name : Symbol(Something.name, Decl(mappedTypesAndObjects.ts, 25, 21)) ->value : Symbol(Something.value, Decl(mappedTypesAndObjects.ts, 25, 35)) +>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 27, 1)) +>name : Symbol(Something.name, Decl(mappedTypesAndObjects.ts, 29, 21)) +>value : Symbol(Something.value, Decl(mappedTypesAndObjects.ts, 29, 35)) interface E2 extends Base { ->E2 : Symbol(E2, Decl(mappedTypesAndObjects.ts, 25, 52)) ->Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1)) +>E2 : Symbol(E2, Decl(mappedTypesAndObjects.ts, 29, 52)) +>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1)) - foo: Partial; // or other mapped type ->foo : Symbol(E2.foo, Decl(mappedTypesAndObjects.ts, 26, 27)) + foo: Partial; // or other mapped type +>foo : Symbol(E2.foo, Decl(mappedTypesAndObjects.ts, 30, 27)) >Partial : Symbol(Partial, Decl(lib.d.ts, --, --)) ->Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 23, 1)) +>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 27, 1)) } interface E3 extends Base { ->E3 : Symbol(E3, Decl(mappedTypesAndObjects.ts, 28, 1)) ->T : Symbol(T, Decl(mappedTypesAndObjects.ts, 30, 13)) ->Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1)) +>E3 : Symbol(E3, Decl(mappedTypesAndObjects.ts, 32, 1)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 34, 13)) +>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1)) - foo: Partial; // or other mapped type ->foo : Symbol(E3.foo, Decl(mappedTypesAndObjects.ts, 30, 30)) + foo: Partial; // or other mapped type +>foo : Symbol(E3.foo, Decl(mappedTypesAndObjects.ts, 34, 30)) >Partial : Symbol(Partial, Decl(lib.d.ts, --, --)) ->T : Symbol(T, Decl(mappedTypesAndObjects.ts, 30, 13)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 34, 13)) } + +// Repro from #13747 + +class Form { +>Form : Symbol(Form, Decl(mappedTypesAndObjects.ts, 36, 1)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11)) + + private values: {[P in keyof T]?: T[P]} = {} +>values : Symbol(Form.values, Decl(mappedTypesAndObjects.ts, 40, 15)) +>P : Symbol(P, Decl(mappedTypesAndObjects.ts, 41, 22)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11)) +>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11)) +>P : Symbol(P, Decl(mappedTypesAndObjects.ts, 41, 22)) +} + diff --git a/tests/baselines/reference/mappedTypesAndObjects.types b/tests/baselines/reference/mappedTypesAndObjects.types index e6f8e53f558..75f71082f31 100644 --- a/tests/baselines/reference/mappedTypesAndObjects.types +++ b/tests/baselines/reference/mappedTypesAndObjects.types @@ -49,19 +49,32 @@ function f2(x: Partial, y: Readonly) { >y : Readonly } +function f3(x: Partial) { +>f3 : (x: Partial) => void +>T : T +>x : Partial +>Partial : Partial +>T : T + + x = {}; +>x = {} : {} +>x : Partial +>{} : {} +} + // Repro from #12900 interface Base { >Base : Base - foo: { [key: string]: any }; + foo: { [key: string]: any }; >foo : { [key: string]: any; } >key : string - bar: any; + bar: any; >bar : any - baz: any; + baz: any; >baz : any } @@ -70,7 +83,7 @@ interface E1 extends Base { >T : T >Base : Base - foo: T; + foo: T; >foo : T >T : T } @@ -84,7 +97,7 @@ interface E2 extends Base { >E2 : E2 >Base : Base - foo: Partial; // or other mapped type + foo: Partial; // or other mapped type >foo : Partial >Partial : Partial >Something : Something @@ -95,8 +108,24 @@ interface E3 extends Base { >T : T >Base : Base - foo: Partial; // or other mapped type + foo: Partial; // or other mapped type >foo : Partial >Partial : Partial >T : T } + +// Repro from #13747 + +class Form { +>Form : Form +>T : T + + private values: {[P in keyof T]?: T[P]} = {} +>values : { [P in keyof T]?: T[P] | undefined; } +>P : P +>T : T +>T : T +>P : P +>{} : {} +} + diff --git a/tests/baselines/reference/mixinClassesAnnotated.js b/tests/baselines/reference/mixinClassesAnnotated.js new file mode 100644 index 00000000000..e3a781033ff --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnnotated.js @@ -0,0 +1,183 @@ +//// [mixinClassesAnnotated.ts] + +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +interface Printable { + print(): void; +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => + class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } + } + +interface Tagged { + _tag: string; +} + +function Tagged>(superClass: T): Constructor & T { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + + +//// [mixinClassesAnnotated.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 Base = (function () { + function Base(x, y) { + this.x = x; + this.y = y; + } + return Base; +}()); +var Derived = (function (_super) { + __extends(Derived, _super); + function Derived(x, y, z) { + var _this = _super.call(this, x, y) || this; + _this.z = z; + return _this; + } + return Derived; +}(Base)); +var Printable = function (superClass) { return _a = (function (_super) { + __extends(class_1, _super); + function class_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + class_1.prototype.print = function () { + var output = this.x + "," + this.y; + }; + return class_1; + }(superClass)), + _a.message = "hello", + _a; var _a; }; +function Tagged(superClass) { + var C = (function (_super) { + __extends(C, _super); + function C() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _this = _super.apply(this, args) || this; + _this._tag = "hello"; + return _this; + } + return C; + }(superClass)); + return C; +} +var Thing1 = Tagged(Derived); +var Thing2 = Tagged(Printable(Derived)); +Thing2.message; +function f1() { + var thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} +function f2() { + var thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} +var Thing3 = (function (_super) { + __extends(Thing3, _super); + function Thing3(tag) { + var _this = _super.call(this, 10, 20, 30) || this; + _this._tag = tag; + return _this; + } + Thing3.prototype.test = function () { + this.print(); + }; + return Thing3; +}(Thing2)); + + +//// [mixinClassesAnnotated.d.ts] +declare type Constructor = new (...args: any[]) => T; +declare class Base { + x: number; + y: number; + constructor(x: number, y: number); +} +declare class Derived extends Base { + z: number; + constructor(x: number, y: number, z: number); +} +interface Printable { + print(): void; +} +declare const Printable: >(superClass: T) => Constructor & { + message: string; +} & T; +interface Tagged { + _tag: string; +} +declare function Tagged>(superClass: T): Constructor & T; +declare const Thing1: Constructor & typeof Derived; +declare const Thing2: Constructor & Constructor & { + message: string; +} & typeof Derived; +declare function f1(): void; +declare function f2(): void; +declare class Thing3 extends Thing2 { + constructor(tag: string); + test(): void; +} diff --git a/tests/baselines/reference/mixinClassesAnnotated.symbols b/tests/baselines/reference/mixinClassesAnnotated.symbols new file mode 100644 index 00000000000..5a03789e2e6 --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnnotated.symbols @@ -0,0 +1,193 @@ +=== tests/cases/conformance/classes/mixinClassesAnnotated.ts === + +type Constructor = new(...args: any[]) => T; +>Constructor : Symbol(Constructor, Decl(mixinClassesAnnotated.ts, 0, 0)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 1, 17)) +>args : Symbol(args, Decl(mixinClassesAnnotated.ts, 1, 26)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 1, 17)) + +class Base { +>Base : Symbol(Base, Decl(mixinClassesAnnotated.ts, 1, 47)) + + constructor(public x: number, public y: number) {} +>x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) +>y : Symbol(Base.y, Decl(mixinClassesAnnotated.ts, 4, 33)) +} + +class Derived extends Base { +>Derived : Symbol(Derived, Decl(mixinClassesAnnotated.ts, 5, 1)) +>Base : Symbol(Base, Decl(mixinClassesAnnotated.ts, 1, 47)) + + constructor(x: number, y: number, public z: number) { +>x : Symbol(x, Decl(mixinClassesAnnotated.ts, 8, 16)) +>y : Symbol(y, Decl(mixinClassesAnnotated.ts, 8, 26)) +>z : Symbol(Derived.z, Decl(mixinClassesAnnotated.ts, 8, 37)) + + super(x, y); +>super : Symbol(Base, Decl(mixinClassesAnnotated.ts, 1, 47)) +>x : Symbol(x, Decl(mixinClassesAnnotated.ts, 8, 16)) +>y : Symbol(y, Decl(mixinClassesAnnotated.ts, 8, 26)) + } +} + +interface Printable { +>Printable : Symbol(Printable, Decl(mixinClassesAnnotated.ts, 11, 1), Decl(mixinClassesAnnotated.ts, 17, 5)) + + print(): void; +>print : Symbol(Printable.print, Decl(mixinClassesAnnotated.ts, 13, 21)) +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => +>Printable : Symbol(Printable, Decl(mixinClassesAnnotated.ts, 11, 1), Decl(mixinClassesAnnotated.ts, 17, 5)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 17, 19)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnnotated.ts, 0, 0)) +>Base : Symbol(Base, Decl(mixinClassesAnnotated.ts, 1, 47)) +>superClass : Symbol(superClass, Decl(mixinClassesAnnotated.ts, 17, 48)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 17, 19)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnnotated.ts, 0, 0)) +>Printable : Symbol(Printable, Decl(mixinClassesAnnotated.ts, 11, 1), Decl(mixinClassesAnnotated.ts, 17, 5)) +>message : Symbol(message, Decl(mixinClassesAnnotated.ts, 17, 90)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 17, 19)) + + class extends superClass { +>superClass : Symbol(superClass, Decl(mixinClassesAnnotated.ts, 17, 48)) + + static message = "hello"; +>message : Symbol((Anonymous class).message, Decl(mixinClassesAnnotated.ts, 18, 30)) + + print() { +>print : Symbol((Anonymous class).print, Decl(mixinClassesAnnotated.ts, 19, 33)) + + const output = this.x + "," + this.y; +>output : Symbol(output, Decl(mixinClassesAnnotated.ts, 21, 17)) +>this.x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) +>this : Symbol((Anonymous class), Decl(mixinClassesAnnotated.ts, 17, 115)) +>x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) +>this.y : Symbol(Base.y, Decl(mixinClassesAnnotated.ts, 4, 33)) +>this : Symbol((Anonymous class), Decl(mixinClassesAnnotated.ts, 17, 115)) +>y : Symbol(Base.y, Decl(mixinClassesAnnotated.ts, 4, 33)) + } + } + +interface Tagged { +>Tagged : Symbol(Tagged, Decl(mixinClassesAnnotated.ts, 23, 5), Decl(mixinClassesAnnotated.ts, 27, 1)) + + _tag: string; +>_tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +} + +function Tagged>(superClass: T): Constructor & T { +>Tagged : Symbol(Tagged, Decl(mixinClassesAnnotated.ts, 23, 5), Decl(mixinClassesAnnotated.ts, 27, 1)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 29, 16)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnnotated.ts, 0, 0)) +>superClass : Symbol(superClass, Decl(mixinClassesAnnotated.ts, 29, 43)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 29, 16)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnnotated.ts, 0, 0)) +>Tagged : Symbol(Tagged, Decl(mixinClassesAnnotated.ts, 23, 5), Decl(mixinClassesAnnotated.ts, 27, 1)) +>T : Symbol(T, Decl(mixinClassesAnnotated.ts, 29, 16)) + + class C extends superClass { +>C : Symbol(C, Decl(mixinClassesAnnotated.ts, 29, 84)) +>superClass : Symbol(superClass, Decl(mixinClassesAnnotated.ts, 29, 43)) + + _tag: string; +>_tag : Symbol(C._tag, Decl(mixinClassesAnnotated.ts, 30, 32)) + + constructor(...args: any[]) { +>args : Symbol(args, Decl(mixinClassesAnnotated.ts, 32, 20)) + + super(...args); +>super : Symbol(T, Decl(mixinClassesAnnotated.ts, 29, 16)) +>args : Symbol(args, Decl(mixinClassesAnnotated.ts, 32, 20)) + + this._tag = "hello"; +>this._tag : Symbol(C._tag, Decl(mixinClassesAnnotated.ts, 30, 32)) +>this : Symbol(C, Decl(mixinClassesAnnotated.ts, 29, 84)) +>_tag : Symbol(C._tag, Decl(mixinClassesAnnotated.ts, 30, 32)) + } + } + return C; +>C : Symbol(C, Decl(mixinClassesAnnotated.ts, 29, 84)) +} + +const Thing1 = Tagged(Derived); +>Thing1 : Symbol(Thing1, Decl(mixinClassesAnnotated.ts, 40, 5)) +>Tagged : Symbol(Tagged, Decl(mixinClassesAnnotated.ts, 23, 5), Decl(mixinClassesAnnotated.ts, 27, 1)) +>Derived : Symbol(Derived, Decl(mixinClassesAnnotated.ts, 5, 1)) + +const Thing2 = Tagged(Printable(Derived)); +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnnotated.ts, 41, 5)) +>Tagged : Symbol(Tagged, Decl(mixinClassesAnnotated.ts, 23, 5), Decl(mixinClassesAnnotated.ts, 27, 1)) +>Printable : Symbol(Printable, Decl(mixinClassesAnnotated.ts, 11, 1), Decl(mixinClassesAnnotated.ts, 17, 5)) +>Derived : Symbol(Derived, Decl(mixinClassesAnnotated.ts, 5, 1)) + +Thing2.message; +>Thing2.message : Symbol(message, Decl(mixinClassesAnnotated.ts, 17, 90)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnnotated.ts, 41, 5)) +>message : Symbol(message, Decl(mixinClassesAnnotated.ts, 17, 90)) + +function f1() { +>f1 : Symbol(f1, Decl(mixinClassesAnnotated.ts, 42, 15)) + + const thing = new Thing1(1, 2, 3); +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 45, 9)) +>Thing1 : Symbol(Thing1, Decl(mixinClassesAnnotated.ts, 40, 5)) + + thing.x; +>thing.x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 45, 9)) +>x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) + + thing._tag; +>thing._tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 45, 9)) +>_tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +} + +function f2() { +>f2 : Symbol(f2, Decl(mixinClassesAnnotated.ts, 48, 1)) + + const thing = new Thing2(1, 2, 3); +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 51, 9)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnnotated.ts, 41, 5)) + + thing.x; +>thing.x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 51, 9)) +>x : Symbol(Base.x, Decl(mixinClassesAnnotated.ts, 4, 16)) + + thing._tag; +>thing._tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 51, 9)) +>_tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) + + thing.print(); +>thing.print : Symbol(Printable.print, Decl(mixinClassesAnnotated.ts, 13, 21)) +>thing : Symbol(thing, Decl(mixinClassesAnnotated.ts, 51, 9)) +>print : Symbol(Printable.print, Decl(mixinClassesAnnotated.ts, 13, 21)) +} + +class Thing3 extends Thing2 { +>Thing3 : Symbol(Thing3, Decl(mixinClassesAnnotated.ts, 55, 1)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnnotated.ts, 41, 5)) + + constructor(tag: string) { +>tag : Symbol(tag, Decl(mixinClassesAnnotated.ts, 58, 16)) + + super(10, 20, 30); + this._tag = tag; +>this._tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +>this : Symbol(Thing3, Decl(mixinClassesAnnotated.ts, 55, 1)) +>_tag : Symbol(Tagged._tag, Decl(mixinClassesAnnotated.ts, 25, 18)) +>tag : Symbol(tag, Decl(mixinClassesAnnotated.ts, 58, 16)) + } + test() { +>test : Symbol(Thing3.test, Decl(mixinClassesAnnotated.ts, 61, 5)) + + this.print(); +>this.print : Symbol(Printable.print, Decl(mixinClassesAnnotated.ts, 13, 21)) +>this : Symbol(Thing3, Decl(mixinClassesAnnotated.ts, 55, 1)) +>print : Symbol(Printable.print, Decl(mixinClassesAnnotated.ts, 13, 21)) + } +} + diff --git a/tests/baselines/reference/mixinClassesAnnotated.types b/tests/baselines/reference/mixinClassesAnnotated.types new file mode 100644 index 00000000000..afaf4c4d25e --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnnotated.types @@ -0,0 +1,224 @@ +=== tests/cases/conformance/classes/mixinClassesAnnotated.ts === + +type Constructor = new(...args: any[]) => T; +>Constructor : Constructor +>T : T +>args : any[] +>T : T + +class Base { +>Base : Base + + constructor(public x: number, public y: number) {} +>x : number +>y : number +} + +class Derived extends Base { +>Derived : Derived +>Base : Base + + constructor(x: number, y: number, public z: number) { +>x : number +>y : number +>z : number + + super(x, y); +>super(x, y) : void +>super : typeof Base +>x : number +>y : number + } +} + +interface Printable { +>Printable : Printable + + print(): void; +>print : () => void +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => +>Printable : >(superClass: T) => Constructor & { message: string; } & T +>>(superClass: T): Constructor & { message: string } & T => class extends superClass { static message = "hello"; print() { const output = this.x + "," + this.y; } } : >(superClass: T) => Constructor & { message: string; } & T +>T : T +>Constructor : Constructor +>Base : Base +>superClass : T +>T : T +>Constructor : Constructor +>Printable : Printable +>message : string +>T : T + + class extends superClass { +>class extends superClass { static message = "hello"; print() { const output = this.x + "," + this.y; } } : { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); message: string; } & T +>superClass : Base + + static message = "hello"; +>message : string +>"hello" : "hello" + + print() { +>print : () => void + + const output = this.x + "," + this.y; +>output : string +>this.x + "," + this.y : string +>this.x + "," : string +>this.x : number +>this : this +>x : number +>"," : "," +>this.y : number +>this : this +>y : number + } + } + +interface Tagged { +>Tagged : Tagged + + _tag: string; +>_tag : string +} + +function Tagged>(superClass: T): Constructor & T { +>Tagged : >(superClass: T) => Constructor & T +>T : T +>Constructor : Constructor +>superClass : T +>T : T +>Constructor : Constructor +>Tagged : Tagged +>T : T + + class C extends superClass { +>C : C +>superClass : {} + + _tag: string; +>_tag : string + + constructor(...args: any[]) { +>args : any[] + + super(...args); +>super(...args) : void +>super : T +>...args : any +>args : any[] + + this._tag = "hello"; +>this._tag = "hello" : "hello" +>this._tag : string +>this : this +>_tag : string +>"hello" : "hello" + } + } + return C; +>C : { new (...args: any[]): C; prototype: Tagged.C; } & T +} + +const Thing1 = Tagged(Derived); +>Thing1 : Constructor & typeof Derived +>Tagged(Derived) : Constructor & typeof Derived +>Tagged : >(superClass: T) => Constructor & T +>Derived : typeof Derived + +const Thing2 = Tagged(Printable(Derived)); +>Thing2 : Constructor & Constructor & { message: string; } & typeof Derived +>Tagged(Printable(Derived)) : Constructor & Constructor & { message: string; } & typeof Derived +>Tagged : >(superClass: T) => Constructor & T +>Printable(Derived) : Constructor & { message: string; } & typeof Derived +>Printable : >(superClass: T) => Constructor & { message: string; } & T +>Derived : typeof Derived + +Thing2.message; +>Thing2.message : string +>Thing2 : Constructor & Constructor & { message: string; } & typeof Derived +>message : string + +function f1() { +>f1 : () => void + + const thing = new Thing1(1, 2, 3); +>thing : Tagged & Derived +>new Thing1(1, 2, 3) : Tagged & Derived +>Thing1 : Constructor & typeof Derived +>1 : 1 +>2 : 2 +>3 : 3 + + thing.x; +>thing.x : number +>thing : Tagged & Derived +>x : number + + thing._tag; +>thing._tag : string +>thing : Tagged & Derived +>_tag : string +} + +function f2() { +>f2 : () => void + + const thing = new Thing2(1, 2, 3); +>thing : Tagged & Printable & Derived +>new Thing2(1, 2, 3) : Tagged & Printable & Derived +>Thing2 : Constructor & Constructor & { message: string; } & typeof Derived +>1 : 1 +>2 : 2 +>3 : 3 + + thing.x; +>thing.x : number +>thing : Tagged & Printable & Derived +>x : number + + thing._tag; +>thing._tag : string +>thing : Tagged & Printable & Derived +>_tag : string + + thing.print(); +>thing.print() : void +>thing.print : () => void +>thing : Tagged & Printable & Derived +>print : () => void +} + +class Thing3 extends Thing2 { +>Thing3 : Thing3 +>Thing2 : Tagged & Printable & Derived + + constructor(tag: string) { +>tag : string + + super(10, 20, 30); +>super(10, 20, 30) : void +>super : Constructor & Constructor & { message: string; } & typeof Derived +>10 : 10 +>20 : 20 +>30 : 30 + + this._tag = tag; +>this._tag = tag : string +>this._tag : string +>this : this +>_tag : string +>tag : string + } + test() { +>test : () => void + + this.print(); +>this.print() : void +>this.print : () => void +>this : this +>print : () => void + } +} + diff --git a/tests/baselines/reference/mixinClassesAnonymous.js b/tests/baselines/reference/mixinClassesAnonymous.js new file mode 100644 index 00000000000..aa148c25acf --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnonymous.js @@ -0,0 +1,160 @@ +//// [mixinClassesAnonymous.ts] +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T) => class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +} + +function Tagged>(superClass: T) { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { + return class extends Base { + timestamp = new Date(); + }; +} + + +//// [mixinClassesAnonymous.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 Base = (function () { + function Base(x, y) { + this.x = x; + this.y = y; + } + return Base; +}()); +var Derived = (function (_super) { + __extends(Derived, _super); + function Derived(x, y, z) { + var _this = _super.call(this, x, y) || this; + _this.z = z; + return _this; + } + return Derived; +}(Base)); +var Printable = function (superClass) { return _a = (function (_super) { + __extends(class_1, _super); + function class_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + class_1.prototype.print = function () { + var output = this.x + "," + this.y; + }; + return class_1; + }(superClass)), + _a.message = "hello", + _a; var _a; }; +function Tagged(superClass) { + var C = (function (_super) { + __extends(C, _super); + function C() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _this = _super.apply(this, args) || this; + _this._tag = "hello"; + return _this; + } + return C; + }(superClass)); + return C; +} +var Thing1 = Tagged(Derived); +var Thing2 = Tagged(Printable(Derived)); +Thing2.message; +function f1() { + var thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} +function f2() { + var thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} +var Thing3 = (function (_super) { + __extends(Thing3, _super); + function Thing3(tag) { + var _this = _super.call(this, 10, 20, 30) || this; + _this._tag = tag; + return _this; + } + Thing3.prototype.test = function () { + this.print(); + }; + return Thing3; +}(Thing2)); +// Repro from #13805 +var Timestamped = function (Base) { + return (function (_super) { + __extends(class_2, _super); + function class_2() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.timestamp = new Date(); + return _this; + } + return class_2; + }(Base)); +}; diff --git a/tests/baselines/reference/mixinClassesAnonymous.symbols b/tests/baselines/reference/mixinClassesAnonymous.symbols new file mode 100644 index 00000000000..bd709501f31 --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnonymous.symbols @@ -0,0 +1,188 @@ +=== tests/cases/conformance/classes/mixinClassesAnonymous.ts === +type Constructor = new(...args: any[]) => T; +>Constructor : Symbol(Constructor, Decl(mixinClassesAnonymous.ts, 0, 0)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 0, 17)) +>args : Symbol(args, Decl(mixinClassesAnonymous.ts, 0, 26)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 0, 17)) + +class Base { +>Base : Symbol(Base, Decl(mixinClassesAnonymous.ts, 0, 47)) + + constructor(public x: number, public y: number) {} +>x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) +>y : Symbol(Base.y, Decl(mixinClassesAnonymous.ts, 3, 33)) +} + +class Derived extends Base { +>Derived : Symbol(Derived, Decl(mixinClassesAnonymous.ts, 4, 1)) +>Base : Symbol(Base, Decl(mixinClassesAnonymous.ts, 0, 47)) + + constructor(x: number, y: number, public z: number) { +>x : Symbol(x, Decl(mixinClassesAnonymous.ts, 7, 16)) +>y : Symbol(y, Decl(mixinClassesAnonymous.ts, 7, 26)) +>z : Symbol(Derived.z, Decl(mixinClassesAnonymous.ts, 7, 37)) + + super(x, y); +>super : Symbol(Base, Decl(mixinClassesAnonymous.ts, 0, 47)) +>x : Symbol(x, Decl(mixinClassesAnonymous.ts, 7, 16)) +>y : Symbol(y, Decl(mixinClassesAnonymous.ts, 7, 26)) + } +} + +const Printable = >(superClass: T) => class extends superClass { +>Printable : Symbol(Printable, Decl(mixinClassesAnonymous.ts, 12, 5)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 12, 19)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnonymous.ts, 0, 0)) +>Base : Symbol(Base, Decl(mixinClassesAnonymous.ts, 0, 47)) +>superClass : Symbol(superClass, Decl(mixinClassesAnonymous.ts, 12, 48)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 12, 19)) +>superClass : Symbol(superClass, Decl(mixinClassesAnonymous.ts, 12, 48)) + + static message = "hello"; +>message : Symbol((Anonymous class).message, Decl(mixinClassesAnonymous.ts, 12, 92)) + + print() { +>print : Symbol((Anonymous class).print, Decl(mixinClassesAnonymous.ts, 13, 29)) + + const output = this.x + "," + this.y; +>output : Symbol(output, Decl(mixinClassesAnonymous.ts, 15, 13)) +>this.x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) +>this : Symbol((Anonymous class), Decl(mixinClassesAnonymous.ts, 12, 65)) +>x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) +>this.y : Symbol(Base.y, Decl(mixinClassesAnonymous.ts, 3, 33)) +>this : Symbol((Anonymous class), Decl(mixinClassesAnonymous.ts, 12, 65)) +>y : Symbol(Base.y, Decl(mixinClassesAnonymous.ts, 3, 33)) + } +} + +function Tagged>(superClass: T) { +>Tagged : Symbol(Tagged, Decl(mixinClassesAnonymous.ts, 17, 1)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 19, 16)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnonymous.ts, 0, 0)) +>superClass : Symbol(superClass, Decl(mixinClassesAnonymous.ts, 19, 43)) +>T : Symbol(T, Decl(mixinClassesAnonymous.ts, 19, 16)) + + class C extends superClass { +>C : Symbol(C, Decl(mixinClassesAnonymous.ts, 19, 59)) +>superClass : Symbol(superClass, Decl(mixinClassesAnonymous.ts, 19, 43)) + + _tag: string; +>_tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) + + constructor(...args: any[]) { +>args : Symbol(args, Decl(mixinClassesAnonymous.ts, 22, 20)) + + super(...args); +>super : Symbol(T, Decl(mixinClassesAnonymous.ts, 19, 16)) +>args : Symbol(args, Decl(mixinClassesAnonymous.ts, 22, 20)) + + this._tag = "hello"; +>this._tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +>this : Symbol(C, Decl(mixinClassesAnonymous.ts, 19, 59)) +>_tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) + } + } + return C; +>C : Symbol(C, Decl(mixinClassesAnonymous.ts, 19, 59)) +} + +const Thing1 = Tagged(Derived); +>Thing1 : Symbol(Thing1, Decl(mixinClassesAnonymous.ts, 30, 5)) +>Tagged : Symbol(Tagged, Decl(mixinClassesAnonymous.ts, 17, 1)) +>Derived : Symbol(Derived, Decl(mixinClassesAnonymous.ts, 4, 1)) + +const Thing2 = Tagged(Printable(Derived)); +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnonymous.ts, 31, 5)) +>Tagged : Symbol(Tagged, Decl(mixinClassesAnonymous.ts, 17, 1)) +>Printable : Symbol(Printable, Decl(mixinClassesAnonymous.ts, 12, 5)) +>Derived : Symbol(Derived, Decl(mixinClassesAnonymous.ts, 4, 1)) + +Thing2.message; +>Thing2.message : Symbol((Anonymous class).message, Decl(mixinClassesAnonymous.ts, 12, 92)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnonymous.ts, 31, 5)) +>message : Symbol((Anonymous class).message, Decl(mixinClassesAnonymous.ts, 12, 92)) + +function f1() { +>f1 : Symbol(f1, Decl(mixinClassesAnonymous.ts, 32, 15)) + + const thing = new Thing1(1, 2, 3); +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 35, 9)) +>Thing1 : Symbol(Thing1, Decl(mixinClassesAnonymous.ts, 30, 5)) + + thing.x; +>thing.x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 35, 9)) +>x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) + + thing._tag; +>thing._tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 35, 9)) +>_tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +} + +function f2() { +>f2 : Symbol(f2, Decl(mixinClassesAnonymous.ts, 38, 1)) + + const thing = new Thing2(1, 2, 3); +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 41, 9)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnonymous.ts, 31, 5)) + + thing.x; +>thing.x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 41, 9)) +>x : Symbol(Base.x, Decl(mixinClassesAnonymous.ts, 3, 16)) + + thing._tag; +>thing._tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 41, 9)) +>_tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) + + thing.print(); +>thing.print : Symbol((Anonymous class).print, Decl(mixinClassesAnonymous.ts, 13, 29)) +>thing : Symbol(thing, Decl(mixinClassesAnonymous.ts, 41, 9)) +>print : Symbol((Anonymous class).print, Decl(mixinClassesAnonymous.ts, 13, 29)) +} + +class Thing3 extends Thing2 { +>Thing3 : Symbol(Thing3, Decl(mixinClassesAnonymous.ts, 45, 1)) +>Thing2 : Symbol(Thing2, Decl(mixinClassesAnonymous.ts, 31, 5)) + + constructor(tag: string) { +>tag : Symbol(tag, Decl(mixinClassesAnonymous.ts, 48, 16)) + + super(10, 20, 30); + this._tag = tag; +>this._tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +>this : Symbol(Thing3, Decl(mixinClassesAnonymous.ts, 45, 1)) +>_tag : Symbol(C._tag, Decl(mixinClassesAnonymous.ts, 20, 32)) +>tag : Symbol(tag, Decl(mixinClassesAnonymous.ts, 48, 16)) + } + test() { +>test : Symbol(Thing3.test, Decl(mixinClassesAnonymous.ts, 51, 5)) + + this.print(); +>this.print : Symbol((Anonymous class).print, Decl(mixinClassesAnonymous.ts, 13, 29)) +>this : Symbol(Thing3, Decl(mixinClassesAnonymous.ts, 45, 1)) +>print : Symbol((Anonymous class).print, Decl(mixinClassesAnonymous.ts, 13, 29)) + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { +>Timestamped : Symbol(Timestamped, Decl(mixinClassesAnonymous.ts, 59, 5)) +>CT : Symbol(CT, Decl(mixinClassesAnonymous.ts, 59, 21)) +>Constructor : Symbol(Constructor, Decl(mixinClassesAnonymous.ts, 0, 0)) +>Base : Symbol(Base, Decl(mixinClassesAnonymous.ts, 59, 53)) +>CT : Symbol(CT, Decl(mixinClassesAnonymous.ts, 59, 21)) + + return class extends Base { +>Base : Symbol(Base, Decl(mixinClassesAnonymous.ts, 59, 53)) + + timestamp = new Date(); +>timestamp : Symbol((Anonymous class).timestamp, Decl(mixinClassesAnonymous.ts, 60, 31)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + }; +} + diff --git a/tests/baselines/reference/mixinClassesAnonymous.types b/tests/baselines/reference/mixinClassesAnonymous.types new file mode 100644 index 00000000000..516b15af2f3 --- /dev/null +++ b/tests/baselines/reference/mixinClassesAnonymous.types @@ -0,0 +1,222 @@ +=== tests/cases/conformance/classes/mixinClassesAnonymous.ts === +type Constructor = new(...args: any[]) => T; +>Constructor : Constructor +>T : T +>args : any[] +>T : T + +class Base { +>Base : Base + + constructor(public x: number, public y: number) {} +>x : number +>y : number +} + +class Derived extends Base { +>Derived : Derived +>Base : Base + + constructor(x: number, y: number, public z: number) { +>x : number +>y : number +>z : number + + super(x, y); +>super(x, y) : void +>super : typeof Base +>x : number +>y : number + } +} + +const Printable = >(superClass: T) => class extends superClass { +>Printable : >(superClass: T) => { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); message: string; } & T +>>(superClass: T) => class extends superClass { static message = "hello"; print() { const output = this.x + "," + this.y; }} : >(superClass: T) => { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); message: string; } & T +>T : T +>Constructor : Constructor +>Base : Base +>superClass : T +>T : T +>class extends superClass { static message = "hello"; print() { const output = this.x + "," + this.y; }} : { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); message: string; } & T +>superClass : Base + + static message = "hello"; +>message : string +>"hello" : "hello" + + print() { +>print : () => void + + const output = this.x + "," + this.y; +>output : string +>this.x + "," + this.y : string +>this.x + "," : string +>this.x : number +>this : this +>x : number +>"," : "," +>this.y : number +>this : this +>y : number + } +} + +function Tagged>(superClass: T) { +>Tagged : >(superClass: T) => { new (...args: any[]): C; prototype: Tagged.C; } & T +>T : T +>Constructor : Constructor +>superClass : T +>T : T + + class C extends superClass { +>C : C +>superClass : {} + + _tag: string; +>_tag : string + + constructor(...args: any[]) { +>args : any[] + + super(...args); +>super(...args) : void +>super : T +>...args : any +>args : any[] + + this._tag = "hello"; +>this._tag = "hello" : "hello" +>this._tag : string +>this : this +>_tag : string +>"hello" : "hello" + } + } + return C; +>C : { new (...args: any[]): C; prototype: Tagged.C; } & T +} + +const Thing1 = Tagged(Derived); +>Thing1 : { new (...args: any[]): Tagged.C; prototype: Tagged.C; } & typeof Derived +>Tagged(Derived) : { new (...args: any[]): Tagged.C; prototype: Tagged.C; } & typeof Derived +>Tagged : >(superClass: T) => { new (...args: any[]): C; prototype: Tagged.C; } & T +>Derived : typeof Derived + +const Thing2 = Tagged(Printable(Derived)); +>Thing2 : { new (...args: any[]): Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C; prototype: Tagged.C; } & { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>Tagged(Printable(Derived)) : { new (...args: any[]): Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C; prototype: Tagged.C; } & { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>Tagged : >(superClass: T) => { new (...args: any[]): C; prototype: Tagged.C; } & T +>Printable(Derived) : { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>Printable : >(superClass: T) => { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); message: string; } & T +>Derived : typeof Derived + +Thing2.message; +>Thing2.message : string +>Thing2 : { new (...args: any[]): Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C; prototype: Tagged.C; } & { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>message : string + +function f1() { +>f1 : () => void + + const thing = new Thing1(1, 2, 3); +>thing : Tagged.C & Derived +>new Thing1(1, 2, 3) : Tagged.C & Derived +>Thing1 : { new (...args: any[]): Tagged.C; prototype: Tagged.C; } & typeof Derived +>1 : 1 +>2 : 2 +>3 : 3 + + thing.x; +>thing.x : number +>thing : Tagged.C & Derived +>x : number + + thing._tag; +>thing._tag : string +>thing : Tagged.C & Derived +>_tag : string +} + +function f2() { +>f2 : () => void + + const thing = new Thing2(1, 2, 3); +>thing : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived +>new Thing2(1, 2, 3) : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived +>Thing2 : { new (...args: any[]): Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C; prototype: Tagged.C; } & { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>1 : 1 +>2 : 2 +>3 : 3 + + thing.x; +>thing.x : number +>thing : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived +>x : number + + thing._tag; +>thing._tag : string +>thing : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived +>_tag : string + + thing.print(); +>thing.print() : void +>thing.print : () => void +>thing : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived +>print : () => void +} + +class Thing3 extends Thing2 { +>Thing3 : Thing3 +>Thing2 : Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C & .(Anonymous class) & Derived + + constructor(tag: string) { +>tag : string + + super(10, 20, 30); +>super(10, 20, 30) : void +>super : { new (...args: any[]): Tagged<{ new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived>.C; prototype: Tagged.C; } & { new (...args: any[]): .(Anonymous class); prototype: .(Anonymous class); message: string; } & typeof Derived +>10 : 10 +>20 : 20 +>30 : 30 + + this._tag = tag; +>this._tag = tag : string +>this._tag : string +>this : this +>_tag : string +>tag : string + } + test() { +>test : () => void + + this.print(); +>this.print() : void +>this.print : () => void +>this : this +>print : () => void + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { +>Timestamped : >(Base: CT) => { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); } & CT +>>(Base: CT) => { return class extends Base { timestamp = new Date(); };} : >(Base: CT) => { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); } & CT +>CT : CT +>Constructor : Constructor +>Base : CT +>CT : CT + + return class extends Base { +>class extends Base { timestamp = new Date(); } : { new (...args: any[]): (Anonymous class); prototype: .(Anonymous class); } & CT +>Base : object + + timestamp = new Date(); +>timestamp : Date +>new Date() : Date +>Date : DateConstructor + + }; +} + diff --git a/tests/baselines/reference/mixinClassesMembers.js b/tests/baselines/reference/mixinClassesMembers.js new file mode 100644 index 00000000000..01fc1bb0032 --- /dev/null +++ b/tests/baselines/reference/mixinClassesMembers.js @@ -0,0 +1,220 @@ +//// [mixinClassesMembers.ts] + +declare class C1 { + public a: number; + protected b: number; + private c: number; + constructor(s: string); + constructor(n: number); +} + +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} + +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} + +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; + +function f1() { + let x1 = new Mixed1("hello"); + let x2 = new Mixed1(42); + let x3 = new Mixed2("hello"); + let x4 = new Mixed2(42); + let x5 = new Mixed3("hello"); + let x6 = new Mixed3(42); + let x7 = new Mixed4("hello"); + let x8 = new Mixed4(42); + let x9 = new Mixed5(); +} + +function f2() { + let x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} + +function f3() { + let x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} + +function f4() { + let x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} + +function f5() { + let x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} + +function f6() { + let x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} + +class C2 extends Mixed1 { + constructor() { + super("hello"); + this.a; + this.b; + this.p; + } +} + +class C3 extends Mixed3 { + constructor() { + super(42); + this.a; + this.b; + this.p; + this.f(); + } + f() { return super.f(); } +} + + +//// [mixinClassesMembers.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 __()); + }; +})(); +function f1() { + var x1 = new Mixed1("hello"); + var x2 = new Mixed1(42); + var x3 = new Mixed2("hello"); + var x4 = new Mixed2(42); + var x5 = new Mixed3("hello"); + var x6 = new Mixed3(42); + var x7 = new Mixed4("hello"); + var x8 = new Mixed4(42); + var x9 = new Mixed5(); +} +function f2() { + var x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} +function f3() { + var x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} +function f4() { + var x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} +function f5() { + var x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} +function f6() { + var x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} +var C2 = (function (_super) { + __extends(C2, _super); + function C2() { + var _this = _super.call(this, "hello") || this; + _this.a; + _this.b; + _this.p; + return _this; + } + return C2; +}(Mixed1)); +var C3 = (function (_super) { + __extends(C3, _super); + function C3() { + var _this = _super.call(this, 42) || this; + _this.a; + _this.b; + _this.p; + _this.f(); + return _this; + } + C3.prototype.f = function () { return _super.prototype.f.call(this); }; + return C3; +}(Mixed3)); + + +//// [mixinClassesMembers.d.ts] +declare class C1 { + a: number; + protected b: number; + private c; + constructor(s: string); + constructor(n: number); +} +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; +declare function f1(): void; +declare function f2(): void; +declare function f3(): void; +declare function f4(): void; +declare function f5(): void; +declare function f6(): void; +declare class C2 extends Mixed1 { + constructor(); +} +declare class C3 extends Mixed3 { + constructor(); + f(): number; +} diff --git a/tests/baselines/reference/mixinClassesMembers.symbols b/tests/baselines/reference/mixinClassesMembers.symbols new file mode 100644 index 00000000000..fabfe7e6d86 --- /dev/null +++ b/tests/baselines/reference/mixinClassesMembers.symbols @@ -0,0 +1,309 @@ +=== tests/cases/conformance/classes/mixinClassesMembers.ts === + +declare class C1 { +>C1 : Symbol(C1, Decl(mixinClassesMembers.ts, 0, 0)) + + public a: number; +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + protected b: number; +>b : Symbol(C1.b, Decl(mixinClassesMembers.ts, 2, 21)) + + private c: number; +>c : Symbol(C1.c, Decl(mixinClassesMembers.ts, 3, 24)) + + constructor(s: string); +>s : Symbol(s, Decl(mixinClassesMembers.ts, 5, 16)) + + constructor(n: number); +>n : Symbol(n, Decl(mixinClassesMembers.ts, 6, 16)) +} + +declare class M1 { +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) + + constructor(...args: any[]); +>args : Symbol(args, Decl(mixinClassesMembers.ts, 10, 16)) + + p: number; +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + static p: number; +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +} + +declare class M2 { +>M2 : Symbol(M2, Decl(mixinClassesMembers.ts, 13, 1)) + + constructor(...args: any[]); +>args : Symbol(args, Decl(mixinClassesMembers.ts, 16, 16)) + + f(): number; +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) + + static f(): number; +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +} + +declare const Mixed1: typeof M1 & typeof C1; +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) +>C1 : Symbol(C1, Decl(mixinClassesMembers.ts, 0, 0)) + +declare const Mixed2: typeof C1 & typeof M1; +>Mixed2 : Symbol(Mixed2, Decl(mixinClassesMembers.ts, 22, 13)) +>C1 : Symbol(C1, Decl(mixinClassesMembers.ts, 0, 0)) +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) + +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) +>M2 : Symbol(M2, Decl(mixinClassesMembers.ts, 13, 1)) +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) +>C1 : Symbol(C1, Decl(mixinClassesMembers.ts, 0, 0)) + +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) +>C1 : Symbol(C1, Decl(mixinClassesMembers.ts, 0, 0)) +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) +>M2 : Symbol(M2, Decl(mixinClassesMembers.ts, 13, 1)) + +declare const Mixed5: typeof M1 & typeof M2; +>Mixed5 : Symbol(Mixed5, Decl(mixinClassesMembers.ts, 25, 13)) +>M1 : Symbol(M1, Decl(mixinClassesMembers.ts, 7, 1)) +>M2 : Symbol(M2, Decl(mixinClassesMembers.ts, 13, 1)) + +function f1() { +>f1 : Symbol(f1, Decl(mixinClassesMembers.ts, 25, 44)) + + let x1 = new Mixed1("hello"); +>x1 : Symbol(x1, Decl(mixinClassesMembers.ts, 28, 7)) +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) + + let x2 = new Mixed1(42); +>x2 : Symbol(x2, Decl(mixinClassesMembers.ts, 29, 7)) +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) + + let x3 = new Mixed2("hello"); +>x3 : Symbol(x3, Decl(mixinClassesMembers.ts, 30, 7)) +>Mixed2 : Symbol(Mixed2, Decl(mixinClassesMembers.ts, 22, 13)) + + let x4 = new Mixed2(42); +>x4 : Symbol(x4, Decl(mixinClassesMembers.ts, 31, 7)) +>Mixed2 : Symbol(Mixed2, Decl(mixinClassesMembers.ts, 22, 13)) + + let x5 = new Mixed3("hello"); +>x5 : Symbol(x5, Decl(mixinClassesMembers.ts, 32, 7)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) + + let x6 = new Mixed3(42); +>x6 : Symbol(x6, Decl(mixinClassesMembers.ts, 33, 7)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) + + let x7 = new Mixed4("hello"); +>x7 : Symbol(x7, Decl(mixinClassesMembers.ts, 34, 7)) +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) + + let x8 = new Mixed4(42); +>x8 : Symbol(x8, Decl(mixinClassesMembers.ts, 35, 7)) +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) + + let x9 = new Mixed5(); +>x9 : Symbol(x9, Decl(mixinClassesMembers.ts, 36, 7)) +>Mixed5 : Symbol(Mixed5, Decl(mixinClassesMembers.ts, 25, 13)) +} + +function f2() { +>f2 : Symbol(f2, Decl(mixinClassesMembers.ts, 37, 1)) + + let x = new Mixed1("hello"); +>x : Symbol(x, Decl(mixinClassesMembers.ts, 40, 7)) +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) + + x.a; +>x.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 40, 7)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + x.p; +>x.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 40, 7)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + Mixed1.p; +>Mixed1.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +} + +function f3() { +>f3 : Symbol(f3, Decl(mixinClassesMembers.ts, 44, 1)) + + let x = new Mixed2("hello"); +>x : Symbol(x, Decl(mixinClassesMembers.ts, 47, 7)) +>Mixed2 : Symbol(Mixed2, Decl(mixinClassesMembers.ts, 22, 13)) + + x.a; +>x.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 47, 7)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + x.p; +>x.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 47, 7)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + Mixed2.p; +>Mixed2.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +>Mixed2 : Symbol(Mixed2, Decl(mixinClassesMembers.ts, 22, 13)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +} + +function f4() { +>f4 : Symbol(f4, Decl(mixinClassesMembers.ts, 51, 1)) + + let x = new Mixed3("hello"); +>x : Symbol(x, Decl(mixinClassesMembers.ts, 54, 7)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) + + x.a; +>x.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 54, 7)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + x.p; +>x.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 54, 7)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + x.f(); +>x.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 54, 7)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) + + Mixed3.p; +>Mixed3.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) + + Mixed3.f(); +>Mixed3.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +} + +function f5() { +>f5 : Symbol(f5, Decl(mixinClassesMembers.ts, 60, 1)) + + let x = new Mixed4("hello"); +>x : Symbol(x, Decl(mixinClassesMembers.ts, 63, 7)) +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) + + x.a; +>x.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 63, 7)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + x.p; +>x.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 63, 7)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + x.f(); +>x.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 63, 7)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) + + Mixed4.p; +>Mixed4.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) + + Mixed4.f(); +>Mixed4.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +>Mixed4 : Symbol(Mixed4, Decl(mixinClassesMembers.ts, 24, 13)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +} + +function f6() { +>f6 : Symbol(f6, Decl(mixinClassesMembers.ts, 69, 1)) + + let x = new Mixed5(); +>x : Symbol(x, Decl(mixinClassesMembers.ts, 72, 7)) +>Mixed5 : Symbol(Mixed5, Decl(mixinClassesMembers.ts, 25, 13)) + + x.p; +>x.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 72, 7)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + x.f(); +>x.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) +>x : Symbol(x, Decl(mixinClassesMembers.ts, 72, 7)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) + + Mixed5.p; +>Mixed5.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) +>Mixed5 : Symbol(Mixed5, Decl(mixinClassesMembers.ts, 25, 13)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 11, 14)) + + Mixed5.f(); +>Mixed5.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +>Mixed5 : Symbol(Mixed5, Decl(mixinClassesMembers.ts, 25, 13)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 17, 16)) +} + +class C2 extends Mixed1 { +>C2 : Symbol(C2, Decl(mixinClassesMembers.ts, 77, 1)) +>Mixed1 : Symbol(Mixed1, Decl(mixinClassesMembers.ts, 21, 13)) + + constructor() { + super("hello"); + this.a; +>this.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>this : Symbol(C2, Decl(mixinClassesMembers.ts, 77, 1)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + this.b; +>this.b : Symbol(C1.b, Decl(mixinClassesMembers.ts, 2, 21)) +>this : Symbol(C2, Decl(mixinClassesMembers.ts, 77, 1)) +>b : Symbol(C1.b, Decl(mixinClassesMembers.ts, 2, 21)) + + this.p; +>this.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>this : Symbol(C2, Decl(mixinClassesMembers.ts, 77, 1)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + } +} + +class C3 extends Mixed3 { +>C3 : Symbol(C3, Decl(mixinClassesMembers.ts, 86, 1)) +>Mixed3 : Symbol(Mixed3, Decl(mixinClassesMembers.ts, 23, 13)) + + constructor() { + super(42); + this.a; +>this.a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) +>this : Symbol(C3, Decl(mixinClassesMembers.ts, 86, 1)) +>a : Symbol(C1.a, Decl(mixinClassesMembers.ts, 1, 18)) + + this.b; +>this.b : Symbol(C1.b, Decl(mixinClassesMembers.ts, 2, 21)) +>this : Symbol(C3, Decl(mixinClassesMembers.ts, 86, 1)) +>b : Symbol(C1.b, Decl(mixinClassesMembers.ts, 2, 21)) + + this.p; +>this.p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) +>this : Symbol(C3, Decl(mixinClassesMembers.ts, 86, 1)) +>p : Symbol(M1.p, Decl(mixinClassesMembers.ts, 10, 32)) + + this.f(); +>this.f : Symbol(C3.f, Decl(mixinClassesMembers.ts, 95, 5)) +>this : Symbol(C3, Decl(mixinClassesMembers.ts, 86, 1)) +>f : Symbol(C3.f, Decl(mixinClassesMembers.ts, 95, 5)) + } + f() { return super.f(); } +>f : Symbol(C3.f, Decl(mixinClassesMembers.ts, 95, 5)) +>super.f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) +>f : Symbol(M2.f, Decl(mixinClassesMembers.ts, 16, 32)) +} + diff --git a/tests/baselines/reference/mixinClassesMembers.types b/tests/baselines/reference/mixinClassesMembers.types new file mode 100644 index 00000000000..c8b29175415 --- /dev/null +++ b/tests/baselines/reference/mixinClassesMembers.types @@ -0,0 +1,352 @@ +=== tests/cases/conformance/classes/mixinClassesMembers.ts === + +declare class C1 { +>C1 : C1 + + public a: number; +>a : number + + protected b: number; +>b : number + + private c: number; +>c : number + + constructor(s: string); +>s : string + + constructor(n: number); +>n : number +} + +declare class M1 { +>M1 : M1 + + constructor(...args: any[]); +>args : any[] + + p: number; +>p : number + + static p: number; +>p : number +} + +declare class M2 { +>M2 : M2 + + constructor(...args: any[]); +>args : any[] + + f(): number; +>f : () => number + + static f(): number; +>f : () => number +} + +declare const Mixed1: typeof M1 & typeof C1; +>Mixed1 : typeof M1 & typeof C1 +>M1 : typeof M1 +>C1 : typeof C1 + +declare const Mixed2: typeof C1 & typeof M1; +>Mixed2 : typeof C1 & typeof M1 +>C1 : typeof C1 +>M1 : typeof M1 + +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>M2 : typeof M2 +>M1 : typeof M1 +>C1 : typeof C1 + +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>C1 : typeof C1 +>M1 : typeof M1 +>M2 : typeof M2 + +declare const Mixed5: typeof M1 & typeof M2; +>Mixed5 : typeof M1 & typeof M2 +>M1 : typeof M1 +>M2 : typeof M2 + +function f1() { +>f1 : () => void + + let x1 = new Mixed1("hello"); +>x1 : M1 & C1 +>new Mixed1("hello") : M1 & C1 +>Mixed1 : typeof M1 & typeof C1 +>"hello" : "hello" + + let x2 = new Mixed1(42); +>x2 : M1 & C1 +>new Mixed1(42) : M1 & C1 +>Mixed1 : typeof M1 & typeof C1 +>42 : 42 + + let x3 = new Mixed2("hello"); +>x3 : C1 & M1 +>new Mixed2("hello") : C1 & M1 +>Mixed2 : typeof C1 & typeof M1 +>"hello" : "hello" + + let x4 = new Mixed2(42); +>x4 : C1 & M1 +>new Mixed2(42) : C1 & M1 +>Mixed2 : typeof C1 & typeof M1 +>42 : 42 + + let x5 = new Mixed3("hello"); +>x5 : M2 & M1 & C1 +>new Mixed3("hello") : M2 & M1 & C1 +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>"hello" : "hello" + + let x6 = new Mixed3(42); +>x6 : M2 & M1 & C1 +>new Mixed3(42) : M2 & M1 & C1 +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>42 : 42 + + let x7 = new Mixed4("hello"); +>x7 : C1 & M1 & M2 +>new Mixed4("hello") : C1 & M1 & M2 +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>"hello" : "hello" + + let x8 = new Mixed4(42); +>x8 : C1 & M1 & M2 +>new Mixed4(42) : C1 & M1 & M2 +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>42 : 42 + + let x9 = new Mixed5(); +>x9 : M1 & M2 +>new Mixed5() : M1 & M2 +>Mixed5 : typeof M1 & typeof M2 +} + +function f2() { +>f2 : () => void + + let x = new Mixed1("hello"); +>x : M1 & C1 +>new Mixed1("hello") : M1 & C1 +>Mixed1 : typeof M1 & typeof C1 +>"hello" : "hello" + + x.a; +>x.a : number +>x : M1 & C1 +>a : number + + x.p; +>x.p : number +>x : M1 & C1 +>p : number + + Mixed1.p; +>Mixed1.p : number +>Mixed1 : typeof M1 & typeof C1 +>p : number +} + +function f3() { +>f3 : () => void + + let x = new Mixed2("hello"); +>x : C1 & M1 +>new Mixed2("hello") : C1 & M1 +>Mixed2 : typeof C1 & typeof M1 +>"hello" : "hello" + + x.a; +>x.a : number +>x : C1 & M1 +>a : number + + x.p; +>x.p : number +>x : C1 & M1 +>p : number + + Mixed2.p; +>Mixed2.p : number +>Mixed2 : typeof C1 & typeof M1 +>p : number +} + +function f4() { +>f4 : () => void + + let x = new Mixed3("hello"); +>x : M2 & M1 & C1 +>new Mixed3("hello") : M2 & M1 & C1 +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>"hello" : "hello" + + x.a; +>x.a : number +>x : M2 & M1 & C1 +>a : number + + x.p; +>x.p : number +>x : M2 & M1 & C1 +>p : number + + x.f(); +>x.f() : number +>x.f : () => number +>x : M2 & M1 & C1 +>f : () => number + + Mixed3.p; +>Mixed3.p : number +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>p : number + + Mixed3.f(); +>Mixed3.f() : number +>Mixed3.f : () => number +>Mixed3 : typeof M2 & typeof M1 & typeof C1 +>f : () => number +} + +function f5() { +>f5 : () => void + + let x = new Mixed4("hello"); +>x : C1 & M1 & M2 +>new Mixed4("hello") : C1 & M1 & M2 +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>"hello" : "hello" + + x.a; +>x.a : number +>x : C1 & M1 & M2 +>a : number + + x.p; +>x.p : number +>x : C1 & M1 & M2 +>p : number + + x.f(); +>x.f() : number +>x.f : () => number +>x : C1 & M1 & M2 +>f : () => number + + Mixed4.p; +>Mixed4.p : number +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>p : number + + Mixed4.f(); +>Mixed4.f() : number +>Mixed4.f : () => number +>Mixed4 : typeof C1 & typeof M1 & typeof M2 +>f : () => number +} + +function f6() { +>f6 : () => void + + let x = new Mixed5(); +>x : M1 & M2 +>new Mixed5() : M1 & M2 +>Mixed5 : typeof M1 & typeof M2 + + x.p; +>x.p : number +>x : M1 & M2 +>p : number + + x.f(); +>x.f() : number +>x.f : () => number +>x : M1 & M2 +>f : () => number + + Mixed5.p; +>Mixed5.p : number +>Mixed5 : typeof M1 & typeof M2 +>p : number + + Mixed5.f(); +>Mixed5.f() : number +>Mixed5.f : () => number +>Mixed5 : typeof M1 & typeof M2 +>f : () => number +} + +class C2 extends Mixed1 { +>C2 : C2 +>Mixed1 : M1 & C1 + + constructor() { + super("hello"); +>super("hello") : void +>super : typeof M1 & typeof C1 +>"hello" : "hello" + + this.a; +>this.a : number +>this : this +>a : number + + this.b; +>this.b : number +>this : this +>b : number + + this.p; +>this.p : number +>this : this +>p : number + } +} + +class C3 extends Mixed3 { +>C3 : C3 +>Mixed3 : M2 & M1 & C1 + + constructor() { + super(42); +>super(42) : void +>super : typeof M2 & typeof M1 & typeof C1 +>42 : 42 + + this.a; +>this.a : number +>this : this +>a : number + + this.b; +>this.b : number +>this : this +>b : number + + this.p; +>this.p : number +>this : this +>p : number + + this.f(); +>this.f() : number +>this.f : () => number +>this : this +>f : () => number + } + f() { return super.f(); } +>f : () => number +>super.f() : number +>super.f : () => number +>super : M2 & M1 & C1 +>f : () => number +} + diff --git a/tests/baselines/reference/nonPrimitiveAssignError.errors.txt b/tests/baselines/reference/nonPrimitiveAssignError.errors.txt index a9bfaaa4a8a..f8c4450f3aa 100644 --- a/tests/baselines/reference/nonPrimitiveAssignError.errors.txt +++ b/tests/baselines/reference/nonPrimitiveAssignError.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(5,1): error TS2322: Type 'object' is not assignable to type '{ foo: string; }'. - Property 'foo' is missing in type 'Object'. + Property 'foo' is missing in type '{}'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(13,1): error TS2322: Type 'number' is not assignable to type 'object'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(14,1): error TS2322: Type 'true' is not assignable to type 'object'. tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(15,1): error TS2322: Type 'string' is not assignable to type 'object'. @@ -16,7 +16,7 @@ tests/cases/conformance/types/nonPrimitive/nonPrimitiveAssignError.ts(19,1): err y = a; // expect error ~ !!! error TS2322: Type 'object' is not assignable to type '{ foo: string; }'. -!!! error TS2322: Property 'foo' is missing in type 'Object'. +!!! error TS2322: Property 'foo' is missing in type '{}'. a = x; a = y; diff --git a/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js b/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js new file mode 100644 index 00000000000..2d86cc0c09c --- /dev/null +++ b/tests/baselines/reference/printerApi/printsBundleCorrectly.default.js @@ -0,0 +1,6 @@ +/*! [a.ts] */ +// comment0 +const a = 1; +/*! [b.ts] */ +// comment1 +const b = 2; diff --git a/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js b/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js new file mode 100644 index 00000000000..22b03719cf3 --- /dev/null +++ b/tests/baselines/reference/printerApi/printsBundleCorrectly.removeComments.js @@ -0,0 +1,4 @@ +/*! [a.ts] */ +const a = 1; +/*! [b.ts] */ +const b = 2; diff --git a/tests/baselines/reference/printerApi/printsFileCorrectly.default.js b/tests/baselines/reference/printerApi/printsFileCorrectly.default.js new file mode 100644 index 00000000000..9bff9d656b3 --- /dev/null +++ b/tests/baselines/reference/printerApi/printsFileCorrectly.default.js @@ -0,0 +1,25 @@ +interface A { + // comment1 + readonly prop?: T; + // comment2 + method(): void; + // comment3 + new (): A; + // comment4 + (): A; +} +// comment5 +type B = number | string | object; +type C = A & { + x: string; +}; // comment6 +// comment7 +enum E1 { + // comment8 + first +} +const enum E2 { + second +} +// comment9 +console.log(1 + 2); diff --git a/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js b/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js new file mode 100644 index 00000000000..b511aff5e78 --- /dev/null +++ b/tests/baselines/reference/printerApi/printsFileCorrectly.removeComments.js @@ -0,0 +1,17 @@ +interface A { + readonly prop?: T; + method(): void; + new (): A; + (): A; +} +type B = number | string | object; +type C = A & { + x: string; +}; +enum E1 { + first +} +const enum E2 { + second +} +console.log(1 + 2); diff --git a/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js b/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js new file mode 100644 index 00000000000..fa19e836fed --- /dev/null +++ b/tests/baselines/reference/printerApi/printsNodeCorrectly.class.js @@ -0,0 +1,3 @@ +class C { + public prop; +} \ No newline at end of file diff --git a/tests/cases/conformance/classes/mixinClassesAnnotated.ts b/tests/cases/conformance/classes/mixinClassesAnnotated.ts new file mode 100644 index 00000000000..62f5e30c9a0 --- /dev/null +++ b/tests/cases/conformance/classes/mixinClassesAnnotated.ts @@ -0,0 +1,67 @@ +// @declaration: true + +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +interface Printable { + print(): void; +} + +const Printable = >(superClass: T): Constructor & { message: string } & T => + class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } + } + +interface Tagged { + _tag: string; +} + +function Tagged>(superClass: T): Constructor & T { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} diff --git a/tests/cases/conformance/classes/mixinClassesAnonymous.ts b/tests/cases/conformance/classes/mixinClassesAnonymous.ts new file mode 100644 index 00000000000..0e422888087 --- /dev/null +++ b/tests/cases/conformance/classes/mixinClassesAnonymous.ts @@ -0,0 +1,64 @@ +type Constructor = new(...args: any[]) => T; + +class Base { + constructor(public x: number, public y: number) {} +} + +class Derived extends Base { + constructor(x: number, y: number, public z: number) { + super(x, y); + } +} + +const Printable = >(superClass: T) => class extends superClass { + static message = "hello"; + print() { + const output = this.x + "," + this.y; + } +} + +function Tagged>(superClass: T) { + class C extends superClass { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = "hello"; + } + } + return C; +} + +const Thing1 = Tagged(Derived); +const Thing2 = Tagged(Printable(Derived)); +Thing2.message; + +function f1() { + const thing = new Thing1(1, 2, 3); + thing.x; + thing._tag; +} + +function f2() { + const thing = new Thing2(1, 2, 3); + thing.x; + thing._tag; + thing.print(); +} + +class Thing3 extends Thing2 { + constructor(tag: string) { + super(10, 20, 30); + this._tag = tag; + } + test() { + this.print(); + } +} + +// Repro from #13805 + +const Timestamped = >(Base: CT) => { + return class extends Base { + timestamp = new Date(); + }; +} diff --git a/tests/cases/conformance/classes/mixinClassesMembers.ts b/tests/cases/conformance/classes/mixinClassesMembers.ts new file mode 100644 index 00000000000..905518c88f8 --- /dev/null +++ b/tests/cases/conformance/classes/mixinClassesMembers.ts @@ -0,0 +1,99 @@ +// @declaration: true + +declare class C1 { + public a: number; + protected b: number; + private c: number; + constructor(s: string); + constructor(n: number); +} + +declare class M1 { + constructor(...args: any[]); + p: number; + static p: number; +} + +declare class M2 { + constructor(...args: any[]); + f(): number; + static f(): number; +} + +declare const Mixed1: typeof M1 & typeof C1; +declare const Mixed2: typeof C1 & typeof M1; +declare const Mixed3: typeof M2 & typeof M1 & typeof C1; +declare const Mixed4: typeof C1 & typeof M1 & typeof M2; +declare const Mixed5: typeof M1 & typeof M2; + +function f1() { + let x1 = new Mixed1("hello"); + let x2 = new Mixed1(42); + let x3 = new Mixed2("hello"); + let x4 = new Mixed2(42); + let x5 = new Mixed3("hello"); + let x6 = new Mixed3(42); + let x7 = new Mixed4("hello"); + let x8 = new Mixed4(42); + let x9 = new Mixed5(); +} + +function f2() { + let x = new Mixed1("hello"); + x.a; + x.p; + Mixed1.p; +} + +function f3() { + let x = new Mixed2("hello"); + x.a; + x.p; + Mixed2.p; +} + +function f4() { + let x = new Mixed3("hello"); + x.a; + x.p; + x.f(); + Mixed3.p; + Mixed3.f(); +} + +function f5() { + let x = new Mixed4("hello"); + x.a; + x.p; + x.f(); + Mixed4.p; + Mixed4.f(); +} + +function f6() { + let x = new Mixed5(); + x.p; + x.f(); + Mixed5.p; + Mixed5.f(); +} + +class C2 extends Mixed1 { + constructor() { + super("hello"); + this.a; + this.b; + this.p; + } +} + +class C3 extends Mixed3 { + constructor() { + super(42); + this.a; + this.b; + this.p; + this.f(); + } + f() { return super.f(); } +} diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 04c26fbe8f7..70b70dff6ea 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -517,3 +517,40 @@ class B extends A<{ x: number}> { p.x; } } + +// Repro from #13749 + +class Form { + private childFormFactories: {[K in keyof T]: (v: T[K]) => Form} + + public set(prop: K, value: T[K]) { + this.childFormFactories[prop](value) + } +} + +// Repro from #13787 + +class SampleClass

{ + public props: Readonly

; + constructor(props: P) { + this.props = Object.freeze(props); + } +} + +interface Foo { + foo: string; +} + +declare function merge(obj1: T, obj2: U): T & U; + +class AnotherSampleClass extends SampleClass { + constructor(props: T) { + const foo: Foo = { foo: "bar" }; + super(merge(props, foo)); + } + + public brokenMethod() { + this.props.foo.concat; + } +} +new AnotherSampleClass({}); diff --git a/tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts b/tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts index 8023f6e7d8d..b3530b46965 100644 --- a/tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts +++ b/tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts @@ -13,23 +13,33 @@ function f2(x: Partial, y: Readonly) { obj = y; } +function f3(x: Partial) { + x = {}; +} + // Repro from #12900 interface Base { - foo: { [key: string]: any }; - bar: any; - baz: any; + foo: { [key: string]: any }; + bar: any; + baz: any; } interface E1 extends Base { - foo: T; + foo: T; } interface Something { name: string, value: string }; interface E2 extends Base { - foo: Partial; // or other mapped type + foo: Partial; // or other mapped type } interface E3 extends Base { - foo: Partial; // or other mapped type -} \ No newline at end of file + foo: Partial; // or other mapped type +} + +// Repro from #13747 + +class Form { + private values: {[P in keyof T]?: T[P]} = {} +} diff --git a/tests/cases/fourslash/completionForStringLiteral10.ts b/tests/cases/fourslash/completionForStringLiteral10.ts new file mode 100644 index 00000000000..76a98eb732f --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral10.ts @@ -0,0 +1,12 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////if ('/**/' != a + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + diff --git a/tests/cases/fourslash/completionForStringLiteral11.ts b/tests/cases/fourslash/completionForStringLiteral11.ts new file mode 100644 index 00000000000..f6acac28a65 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral11.ts @@ -0,0 +1,14 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////switch (a) { +//// case '/**/ +////} + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + diff --git a/tests/cases/fourslash/completionForStringLiteral8.ts b/tests/cases/fourslash/completionForStringLiteral8.ts new file mode 100644 index 00000000000..727bdf6e6a4 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral8.ts @@ -0,0 +1,12 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////if (a === '/**/ + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + diff --git a/tests/cases/fourslash/goToDefinitionFoo.ts b/tests/cases/fourslash/goToDefinitionFoo.ts deleted file mode 100644 index 3e05ee20ecb..00000000000 --- a/tests/cases/fourslash/goToDefinitionFoo.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// - -////function /*def*/f() {} -/////*use*/f(123); - -verify.goToDefinition("use", "def"); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts index 0223d96e018..9f6cac0b7c1 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile1.ts @@ -3,7 +3,7 @@ //// [|/// //// f1/*0*/();|] -// @Filename: module.ts +// @Filename: Module.ts //// export function f1() {} //// export var v1 = 5; @@ -12,7 +12,7 @@ verify.importFixAtPosition([ `/// -import { f1 } from "./module"; +import { f1 } from "./Module"; f1();` ]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts new file mode 100644 index 00000000000..af7901e03e6 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts @@ -0,0 +1,15 @@ +/// + +//// [|let t: XXX/*0*/.I;|] + +// @Filename: ./module.ts +//// export module XXX { +//// export interface I { +//// } +//// } + +verify.importFixAtPosition([ +`import { XXX } from "./module"; + +let t: XXX.I;` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts new file mode 100644 index 00000000000..62cf48c4977 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts @@ -0,0 +1,16 @@ +/// + +//// [|let t: A/*0*/.B.I;|] + +// @Filename: ./module.ts +//// export namespace A { +//// export namespace B { +//// export interface I { } +//// } +//// } + +verify.importFixAtPosition([ +`import { A } from "./module"; + +let t: A.B.I;` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/unusedFunctionInNamespace1.ts b/tests/cases/fourslash/unusedFunctionInNamespace1.ts index 8ebf1f7072a..288573608f2 100644 --- a/tests/cases/fourslash/unusedFunctionInNamespace1.ts +++ b/tests/cases/fourslash/unusedFunctionInNamespace1.ts @@ -2,9 +2,11 @@ // @noUnusedLocals: true //// [| namespace greeter { +//// // some legit comments //// function function1() { //// }/*1*/ //// } |] verify.rangeAfterCodeFix(`namespace greeter { + // some legit comments }`); diff --git a/tests/cases/fourslash/unusedImports3FS.ts b/tests/cases/fourslash/unusedImports3FS.ts index 98dc30c4336..6926f97bb4f 100644 --- a/tests/cases/fourslash/unusedImports3FS.ts +++ b/tests/cases/fourslash/unusedImports3FS.ts @@ -2,7 +2,7 @@ // @noUnusedLocals: true // @Filename: file2.ts -////[| import {Calculator, test, test2} from "./file1" |] +////[| import {Calculator, /*some comments*/ test, test2} from "./file1" |] //// test(); //// test2(); @@ -20,5 +20,5 @@ //// //// } -verify.rangeAfterCodeFix(`import {test, test2} from "./file1"`); +verify.rangeAfterCodeFix(`import {/*some comments*/ test, test2} from "./file1"`);