Skip to content

Commit

Permalink
add support for parameterised types
Browse files Browse the repository at this point in the history
Fixes #58.
  • Loading branch information
43081j committed Jan 8, 2018
1 parent 29f2b4e commit c934726
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Support parameterised types other than `Array` and `Object`, such as `Foo<T>`

## [0.3.5] - 2017-01-01
- Properties are now emitted as `readonly` when applicable.
Expand Down
20 changes: 20 additions & 0 deletions src/closure-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ function convert(node: doctrine.Type, templateTypes: string[]): ts.Type {
t = convertArray(node, templateTypes);
} else if (isParameterizedObject(node)) { // Object<foo, bar>
t = convertIndexableObject(node, templateTypes);
} else if (isParameterizedType(node)) { // Type<T>
t = convertParameterizedType(node, templateTypes);
} else if (isUnion(node)) { // foo|bar
t = convertUnion(node, templateTypes);
} else if (isFunction(node)) { // function(foo): bar
Expand Down Expand Up @@ -229,6 +231,18 @@ function convertIndexableObject(
convert(node.applications[1], templateTypes));
}

function convertParameterizedType(
node: doctrine.type.TypeApplication,
templateTypes: string[]): ts.ParameterizedType|ts.NameType {
if (!isName(node.expression)) {
console.error('Could not find name of parameterized type');
return ts.anyType;
}
const types = node.applications.map((application) =>
convert(application, templateTypes));
return new ts.ParameterizedType(node.expression.name, ...types);
}

function convertUnion(
node: doctrine.type.UnionType, templateTypes: string[]): ts.Type {
return new ts.UnionType(
Expand Down Expand Up @@ -300,6 +314,12 @@ function isParameterizedArray(node: doctrine.Type):
node.expression.name === 'Array';
}

function isParameterizedType(node: doctrine.Type):
node is doctrine.type.TypeApplication {
return node.type === 'TypeApplication' &&
node.expression.type === 'NameExpression';
}

function isBareArray(node: doctrine.Type):
node is doctrine.type.TypeApplication {
return node.type === 'NameExpression' && node.name === 'Array';
Expand Down
11 changes: 11 additions & 0 deletions src/test/fixtures/custom/my-class.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* @template T
* @constructor
*/
function MyParameterizedType() {}

class MyClass {
no_params() { }

Expand Down Expand Up @@ -42,4 +48,9 @@ class MyClass {
defaulted_and_optional_param(p1 = "foo", p2) { }

defaulted_and_required_param(p1 = "foo", p2) { }

/**
* @returns {!MyParameterizedType<boolean>}
*/
parameterized_return_type() { }
}
1 change: 1 addition & 0 deletions src/test/goldens/custom/my-class.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ declare class MyClass {
defaulted_param(p1?: any): any;
defaulted_and_optional_param(p1?: any, p2?: string): any;
defaulted_and_required_param(p1: any|undefined, p2: any): any;
parameterized_return_type(): MyParameterizedType<boolean>;
}
26 changes: 25 additions & 1 deletion src/ts-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export class Property {

// A TypeScript type expression.
export type Type = NameType|UnionType|ArrayType|FunctionType|ConstructorType|
RecordType|IntersectionType|IndexableObjectType|ParamType;
RecordType|IntersectionType|IndexableObjectType|ParamType|ParameterizedType;

// string, MyClass, null, undefined, any
export class NameType {
Expand Down Expand Up @@ -513,6 +513,30 @@ export class ArrayType {
}
}

// Foo<Bar>
export class ParameterizedType {
readonly kind = 'parameterized';
itemTypes: Type[];
name: string;

constructor(name: string, ...itemTypes: Type[]) {
this.name = name;
this.itemTypes = itemTypes;
}

* traverse(): Iterable<Node> {
for (const itemType of this.itemTypes) {
yield* itemType.traverse();
}
yield this;
}

serialize(): string {
const types = this.itemTypes.map((t) => t.serialize());
return `${this.name}<${types.join(', ')}>`;
}
}

// (foo: bar) => baz
export class FunctionType {
readonly kind = 'function';
Expand Down

0 comments on commit c934726

Please sign in to comment.