diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 48a5933a59a..e12036322bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -81,6 +81,7 @@ namespace ts { getIndexTypeOfType, getBaseTypes, getReturnTypeOfSignature, + resolveStructuredTypeMembers, getNonNullableType, getSymbolsInScope, getSymbolAtLocation, @@ -6586,10 +6587,12 @@ namespace ts { } } - // Compare two types and return - // Ternary.True if they are related with no assumptions, - // Ternary.Maybe if they are related with assumptions of other relationships, or - // Ternary.False if they are not related. + /** + * Compare two types and return + * * Ternary.True if they are related with no assumptions, + * * Ternary.Maybe if they are related with assumptions of other relationships, or + * * Ternary.False if they are not related. + */ function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary { let result: Ternary; if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cefa3714017..ece3dab6ecc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2251,6 +2251,7 @@ namespace ts { getSignaturesOfType(type: Type, kind: SignatureKind): Signature[]; getIndexTypeOfType(type: Type, kind: IndexKind): Type; getBaseTypes(type: InterfaceType): ObjectType[]; + resolveStructuredTypeMembers(type: StructuredType): ResolvedType; getReturnTypeOfSignature(signature: Signature): Type; getNonNullableType(type: Type): Type; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 1f4f07e96ac..3777d8b8ac3 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -372,7 +372,7 @@ namespace ts { export function isThis(node: Node): boolean { switch (node.kind) { case SyntaxKind.ThisKeyword: - // case SyntaxKind.ThisType: TODO: GH#9267 + // case SyntaxKind.ThisType: TODO: GH#9267 return true; case SyntaxKind.Identifier: // 'this' as a parameter @@ -1359,6 +1359,90 @@ namespace ts { }; } + /* + const classMembers: TypeElement[]; // TODO: this + const implementedInterfaceMembers: TypeElement[] = []; + const parentAbstractMembers: TypeElement[] = [] + const missingMembers: TypeElement[] = []; + // const typeWiththis = checker.getTypeWithThisArgument(classType); + // const staticType = checker.getTypeOfSymbol(symbol); + // const classType = checker.getTypeAtLocation(classDecl); + */ + + export function getUnimplementedMemberChanges(classDecl: ClassDeclaration, checker: TypeChecker): TextChange[] { + const baseTypeNode: ExpressionWithTypeArguments = getClassExtendsHeritageClauseElement(classDecl); + + if (!baseTypeNode) + { + return []; + } + const classSymbol = checker.getSymbolAtLocation(classDecl); + const classType = checker.getDeclaredTypeOfSymbol(classSymbol); + + const baseTypes = checker.getBaseTypes(classType); + Debug.assert(baseTypes.length === 1); + const baseType = baseTypes[0]; + + const resolvedClassType = checker.resolveStructuredTypeMembers(classType); + const resolvedBaseType = checker.resolveStructuredTypeMembers(baseType); + + const missingMembers = filterMissingMembers(resolvedClassType.members, resolvedBaseType.members); + return insertionsForMembers(missingMembers); + } + + // TODO: (arozga) Get changes for interface as well. + // const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl); + } + + function insertionsForMembers(symbols: Symbol[]): TextChange[] { + let changes: TextChange[] = []; + for (const symbol of symbols) { + const decl = getdeclaration + switch (member.kind) { + case SyntaxKind.PropertySignature: + case SyntaxKind.PropertyDeclaration: + break; + case SyntaxKind.MethodSignature: + case SyntaxKind.MethodDeclaration: + break; + default: + break; + } + } + return changes; + } + + // TODO: (arozga) simplify to quadratic time solution. + function filterMissingMembers(sourceSymbols: Map, targetSymbols: Map): Symbol[] { + let missingMembers: Symbol[] = []; + const sortedSourceKeys = Object.keys(sourceSymbols).sort(); + const sortedTargetKeys = Object.keys(targetSymbols).sort(); + + let i = 0; + let j = 0; + while (i < sortedSourceKeys.length && j < sortedTargetKeys.length) { + // TODO: (arozga) convert switch to if else with inequalities (browser compat?) + switch (sortedSourceKeys[i].localeCompare(sortedSourceKeys[j])) { + case 0: + ++i; + ++j; + break; + case -1: + missingMembers.push(sourceSymbols[sortedSourceKeys[i]]); + ++i; + break; + case 1: + ++j; + default: + } + } + + return missingMembers; + } + + /** + * Generates codefix changes to insert + */ export function getCodeFixChanges(interfaceClause: Node, existingMembers: string[], startPos: number, checker: TypeChecker, reference: boolean, trackingAddedMembers: string[], newLineCharacter: string): TextChange[] { const type = checker.getTypeAtLocation(interfaceClause); @@ -1366,7 +1450,7 @@ namespace ts { return []; } - const missingMembers = getMissingInterfaceMembers(type.symbol.declarations[0], existingMembers, checker); + const missingMembers: TypeElement[] = getMissingInterfaceMembers(type.symbol.declarations[0], existingMembers, checker); const changesArray: TextChange[] = []; for (const member of missingMembers) { @@ -1405,7 +1489,7 @@ namespace ts { * Gets members in an interface and all its base types. */ function getInterfaceMembers(declaration: InterfaceDeclaration, checker: TypeChecker): TypeElement[] { - let result: TypeElement[] = declaration.members + let result: TypeElement[] = declaration.members ? declaration.members.filter(member => !(getModifierFlags(member) & ModifierFlags.Private)) : []; const clauses = getInterfaceBaseTypeNodes(declaration); @@ -1424,7 +1508,6 @@ namespace ts { function getMissingInterfaceMembers(declaration: InterfaceDeclaration, existingMembers: string[], checker: TypeChecker): TypeElement[] { const interfaceMembers = getInterfaceMembers(declaration, checker); return ts.filter(interfaceMembers, member => !member.name || existingMembers.indexOf(member.name.getText()) === -1); - } export function getNamedClassMembers(classDeclaration: ClassDeclaration): ClassElement[] {