feat(ivy): add `getBaseClassIdentifier()` to `ReflectionHost` (#31544)
This method will be useful for writing ngcc `Migrations` that need to be able to find base classes. PR Close #31544
This commit is contained in:
parent
399935c32b
commit
8a470b9af9
|
@ -190,9 +190,21 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return innerClassDeclaration.heritageClauses !== undefined &&
|
return super.hasBaseClass(innerClassDeclaration);
|
||||||
innerClassDeclaration.heritageClauses.some(
|
}
|
||||||
clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
|
||||||
|
getBaseClassExpression(clazz: ClassDeclaration): ts.Expression|null {
|
||||||
|
// First try getting the base class from the "outer" declaration
|
||||||
|
const superBaseClassIdentifier = super.getBaseClassExpression(clazz);
|
||||||
|
if (superBaseClassIdentifier) {
|
||||||
|
return superBaseClassIdentifier;
|
||||||
|
}
|
||||||
|
// That didn't work so now try getting it from the "inner" declaration.
|
||||||
|
const innerClassDeclaration = getInnerClassDeclaration(clazz);
|
||||||
|
if (innerClassDeclaration === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.getBaseClassExpression(innerClassDeclaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -56,6 +56,32 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||||
return iife.parameters.length === 1 && isSuperIdentifier(iife.parameters[0].name);
|
return iife.parameters.length === 1 && isSuperIdentifier(iife.parameters[0].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBaseClassExpression(clazz: ClassDeclaration): ts.Expression|null {
|
||||||
|
const superBaseClassIdentifier = super.getBaseClassExpression(clazz);
|
||||||
|
if (superBaseClassIdentifier) {
|
||||||
|
return superBaseClassIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classDeclaration = this.getClassDeclaration(clazz);
|
||||||
|
if (!classDeclaration) return null;
|
||||||
|
|
||||||
|
const iifeBody = getIifeBody(classDeclaration);
|
||||||
|
if (!iifeBody) return null;
|
||||||
|
|
||||||
|
const iife = iifeBody.parent;
|
||||||
|
if (!iife || !ts.isFunctionExpression(iife)) return null;
|
||||||
|
|
||||||
|
if (iife.parameters.length !== 1 || !isSuperIdentifier(iife.parameters[0].name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ts.isCallExpression(iife.parent)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iife.parent.arguments[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the declaration of a class given a node that we think represents the class.
|
* Find the declaration of a class given a node that we think represents the class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1855,6 +1855,95 @@ exports.ExternalModule = ExternalModule;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getBaseClassExpression()', () => {
|
||||||
|
function getBaseClassIdentifier(source: string): ts.Identifier|null {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: source,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program, host: compilerHost} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode);
|
||||||
|
if (expression !== null && !ts.isIdentifier(expression)) {
|
||||||
|
throw new Error(
|
||||||
|
'Expected class to inherit via an identifier but got: ' + expression.getText());
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with _super parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with a unique name generated for the _super parameter',
|
||||||
|
() => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super_1) {
|
||||||
|
__extends(TestClass, _super_1);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not find a base class for an IIFE without parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function () {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a dynamic base class expression of an IIFE', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: `
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
function foo() { return BaseClass; }
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(foo()));`,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program, host: compilerHost} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode) !;
|
||||||
|
expect(expression.getText()).toBe('foo()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('findClassSymbols()', () => {
|
describe('findClassSymbols()', () => {
|
||||||
it('should return an array of all classes in the given source file', () => {
|
it('should return an array of all classes in the given source file', () => {
|
||||||
loadTestFiles(DECORATED_FILES);
|
loadTestFiles(DECORATED_FILES);
|
||||||
|
|
|
@ -1680,6 +1680,77 @@ runInEachFileSystem(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getBaseClassExpression()', () => {
|
||||||
|
it('should not consider a class without extends clause as having a base class', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/base_class.js'),
|
||||||
|
contents: `class TestClass {}`,
|
||||||
|
};
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedClassDeclaration);
|
||||||
|
expect(host.getBaseClassExpression(classNode)).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class of a class with an `extends` clause', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/base_class.js'),
|
||||||
|
contents: `
|
||||||
|
class BaseClass {}
|
||||||
|
class TestClass extends BaseClass {}`,
|
||||||
|
};
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode = getDeclaration(program, file.name, 'TestClass', isNamedClassDeclaration);
|
||||||
|
const baseIdentifier = host.getBaseClassExpression(classNode) !;
|
||||||
|
if (!ts.isIdentifier(baseIdentifier)) {
|
||||||
|
throw new Error(`Expected ${baseIdentifier.getText()} to be an identifier.`);
|
||||||
|
}
|
||||||
|
expect(baseIdentifier.text).toEqual('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class of an aliased class with an `extends` clause', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/base_class.js'),
|
||||||
|
contents: `
|
||||||
|
let TestClass_1;
|
||||||
|
class BaseClass {}
|
||||||
|
let TestClass = TestClass_1 = class TestClass extends BaseClass {}`,
|
||||||
|
};
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const baseIdentifier = host.getBaseClassExpression(classNode) !;
|
||||||
|
if (!ts.isIdentifier(baseIdentifier)) {
|
||||||
|
throw new Error(`Expected ${baseIdentifier.getText()} to be an identifier.`);
|
||||||
|
}
|
||||||
|
expect(baseIdentifier.text).toEqual('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class expression of a class with a dynamic `extends` expression',
|
||||||
|
() => {
|
||||||
|
const file = {
|
||||||
|
name: _('/base_class.js'),
|
||||||
|
contents: `
|
||||||
|
class BaseClass {}
|
||||||
|
function foo() { return BaseClass; }
|
||||||
|
class TestClass extends foo() {}`,
|
||||||
|
};
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host =
|
||||||
|
new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedClassDeclaration);
|
||||||
|
const baseExpression = host.getBaseClassExpression(classNode) !;
|
||||||
|
expect(baseExpression.getText()).toEqual('foo()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getGenericArityOfClass()', () => {
|
describe('getGenericArityOfClass()', () => {
|
||||||
it('should properly count type parameters', () => {
|
it('should properly count type parameters', () => {
|
||||||
loadTestFiles(ARITY_CLASSES);
|
loadTestFiles(ARITY_CLASSES);
|
||||||
|
|
|
@ -2022,6 +2022,95 @@ runInEachFileSystem(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getBaseClassExpression()', () => {
|
||||||
|
function getBaseClassIdentifier(source: string): ts.Identifier|null {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: source,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode);
|
||||||
|
if (expression !== null && !ts.isIdentifier(expression)) {
|
||||||
|
throw new Error(
|
||||||
|
'Expected class to inherit via an identifier but got: ' + expression.getText());
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with _super parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with a unique name generated for the _super parameter',
|
||||||
|
() => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super_1) {
|
||||||
|
__extends(TestClass, _super_1);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not find a base class for an IIFE without parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function () {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a dynamic base class expression of an IIFE', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: `
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
function foo() { return BaseClass; }
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(foo()));`,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker());
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode) !;
|
||||||
|
expect(expression.getText()).toBe('foo()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('findClassSymbols()', () => {
|
describe('findClassSymbols()', () => {
|
||||||
it('should return an array of all classes in the given source file', () => {
|
it('should return an array of all classes in the given source file', () => {
|
||||||
loadTestFiles(DECORATED_FILES);
|
loadTestFiles(DECORATED_FILES);
|
||||||
|
|
|
@ -1934,6 +1934,95 @@ runInEachFileSystem(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getBaseClassExpression()', () => {
|
||||||
|
function getBaseClassIdentifier(source: string): ts.Identifier|null {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: source,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program, host: compilerHost} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode);
|
||||||
|
if (expression !== null && !ts.isIdentifier(expression)) {
|
||||||
|
throw new Error(
|
||||||
|
'Expected class to inherit via an identifier but got: ' + expression.getText());
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with _super parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find the base class of an IIFE with a unique name generated for the _super parameter',
|
||||||
|
() => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function (_super_1) {
|
||||||
|
__extends(TestClass, _super_1);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier !.text).toBe('BaseClass');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not find a base class for an IIFE without parameter', () => {
|
||||||
|
const identifier = getBaseClassIdentifier(`
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
var TestClass = /** @class */ (function () {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(BaseClass));`);
|
||||||
|
expect(identifier).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find a dynamic base class expression of an IIFE', () => {
|
||||||
|
const file = {
|
||||||
|
name: _('/synthesized_constructors.js'),
|
||||||
|
contents: `
|
||||||
|
var BaseClass = /** @class */ (function () {
|
||||||
|
function BaseClass() {}
|
||||||
|
return BaseClass;
|
||||||
|
}());
|
||||||
|
function foo() { return BaseClass; }
|
||||||
|
var TestClass = /** @class */ (function (_super) {
|
||||||
|
__extends(TestClass, _super);
|
||||||
|
function TestClass() {}
|
||||||
|
return TestClass;
|
||||||
|
}(foo()));`,
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTestFiles([file]);
|
||||||
|
const {program, host: compilerHost} = makeTestBundleProgram(file.name);
|
||||||
|
const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost);
|
||||||
|
const classNode =
|
||||||
|
getDeclaration(program, file.name, 'TestClass', isNamedVariableDeclaration);
|
||||||
|
const expression = host.getBaseClassExpression(classNode) !;
|
||||||
|
expect(expression.getText()).toBe('foo()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('findClassSymbols()', () => {
|
describe('findClassSymbols()', () => {
|
||||||
it('should return an array of all classes in the given source file', () => {
|
it('should return an array of all classes in the given source file', () => {
|
||||||
loadTestFiles(DECORATED_FILES);
|
loadTestFiles(DECORATED_FILES);
|
||||||
|
|
|
@ -505,6 +505,16 @@ export interface ReflectionHost {
|
||||||
*/
|
*/
|
||||||
hasBaseClass(clazz: ClassDeclaration): boolean;
|
hasBaseClass(clazz: ClassDeclaration): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an expression representing the base class (if any) of the given `clazz`.
|
||||||
|
*
|
||||||
|
* This expression is most commonly an Identifier, but is possible to inherit from a more dynamic
|
||||||
|
* expression.
|
||||||
|
*
|
||||||
|
* @param clazz the class whose base we want to get.
|
||||||
|
*/
|
||||||
|
getBaseClassExpression(clazz: ClassDeclaration): ts.Expression|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of generic type parameters of a given class.
|
* Get the number of generic type parameters of a given class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -121,10 +121,28 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasBaseClass(clazz: ClassDeclaration): boolean {
|
hasBaseClass(clazz: ClassDeclaration): boolean {
|
||||||
return ts.isClassDeclaration(clazz) && clazz.heritageClauses !== undefined &&
|
return (ts.isClassDeclaration(clazz) || ts.isClassExpression(clazz)) &&
|
||||||
|
clazz.heritageClauses !== undefined &&
|
||||||
clazz.heritageClauses.some(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
clazz.heritageClauses.some(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBaseClassExpression(clazz: ClassDeclaration): ts.Expression|null {
|
||||||
|
if (!(ts.isClassDeclaration(clazz) || ts.isClassExpression(clazz)) ||
|
||||||
|
clazz.heritageClauses === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const extendsClause =
|
||||||
|
clazz.heritageClauses.find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
|
||||||
|
if (extendsClause === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const extendsType = extendsClause.types[0];
|
||||||
|
if (extendsType === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return extendsType.expression;
|
||||||
|
}
|
||||||
|
|
||||||
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
|
||||||
// Resolve the identifier to a Symbol, and return the declaration of that.
|
// Resolve the identifier to a Symbol, and return the declaration of that.
|
||||||
let symbol: ts.Symbol|undefined = this.checker.getSymbolAtLocation(id);
|
let symbol: ts.Symbol|undefined = this.checker.getSymbolAtLocation(id);
|
||||||
|
|
Loading…
Reference in New Issue