-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic generics support #37
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High level review, structurally I think it looks pretty good. As to whether we think it's a good idea to support, I leave that to others ;)
Only niggle on the terminology side is that it's really "templated type" / "type templates" (affecting only the type system) rather than "generics" (tends to mean generated code per type in most contexts)
"port": 9229 | ||
} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for setting this up!
for (const clause of node.typeParameters) { | ||
const type = this.checker.getTypeAtLocation(clause); | ||
const symbol = type.getSymbol(); | ||
if (symbol) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, in what case would you not have a symbol here? (seems like it might be worth an assertion that the symbol exists?)
if (typeRef.type === 'expressionWithTypeNode' && typeRef.referenceNodes.some(r => r.type === 'reference' && r.referenceNodes.length > 0)) { | ||
console.error(type); | ||
console.error(type.getSourceFile().fileName); | ||
throw new Error(`Generic inheritance cannot have another generic. Try using a named type instead. ${node.getText()}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No nested generics? booo! :P
console.error(type.getSourceFile().fileName); | ||
throw new Error(`Generic inheritance cannot have another generic. Try using a named type instead. ${node.getText()}`); | ||
} | ||
// get the symbol + typeref. if it's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, you're leaving me hanging here
} | ||
|
||
return this._addType(node, () => { | ||
const inherits = []; | ||
const typeParameters: string[] = []; | ||
if (node.heritageClauses) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future PRs, you might also want to support type aliases. E.g.
type StringNumberArray = MultipleGenerics<string, number>;
(if we are likely to do this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could add a TODO comment for this if you plan to punt for now, @heyitsaamir
if (m.type === 'property') { | ||
if (m.signature.type === 'genericPropertyNode' && m.signature.signature === t) { | ||
newGenericMapForNode.set(m.name, referenceNode) | ||
} else if (m.signature.type === 'array') { | ||
const arrayType = m.signature.elements[0]; | ||
if (arrayType.type === 'genericPropertyNode' && arrayType.signature === t) { | ||
newGenericMapForNode.set(m.name, referenceNode) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may end up wanting to make this its own (recursive) visitor in order to handle more complex types if we see them in use (e.g. things like foo: { bar: TSomeTemplateType }
if (m.signature.type === 'genericPropertyNode' && m.signature.signature === t) { | ||
newGenericMapForNode.set(m.name, referenceNode) | ||
} else if (m.signature.type === 'array') { | ||
const arrayType = m.signature.elements[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely will want to handle other elements (unions, I think) or assert this is the only case here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! LGTM so far.
source ./scripts/include/node.sh | ||
|
||
npm run clean | ||
tsc-watch "${@}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
} | ||
|
||
return this._addType(node, () => { | ||
const inherits = []; | ||
const typeParameters: string[] = []; | ||
if (node.heritageClauses) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could add a TODO comment for this if you plan to punt for now, @heyitsaamir
_referenceForSymbol(symbol:typescript.Symbol):types.ReferenceNode { | ||
this._walkSymbol(symbol); | ||
_referenceForSymbol(symbol: typescript.Symbol, node: typescript.TypeReferenceNode): types.ReferenceNode { | ||
this._walkSymbol(symbol); //getTypeReferenceName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? delete?
return `enum ${this._name(name)} {\n${this._indent(node.values)}\n}`; | ||
} | ||
|
||
_emitExpression = (node:Types.Node):string => { | ||
_emitExpression = (node: Types.Node, possibleGenericReferenceNode?: Types.ReferenceNode): string => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why have the possible
prefix? The ?
already indicates the param may be undefined
}) | ||
.join(', '); | ||
} else if (node.type === 'genericPropertyNode') { | ||
if (!possibleGenericReferenceNode) { | ||
throw new Error(`node ${node.signature} was not resolved`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what case is this expected? This error message doesn't point at the cause or resolution.
@@ -219,7 +234,7 @@ export default class Emitter { | |||
if (interfaceNode.inherits.length > 1) { | |||
throw new Error(`No support for multiple inheritence: ${JSON.stringify(interfaceNode.inherits)}`); | |||
} else if (interfaceNode.inherits.length === 1) { | |||
const supertype:Types.Node = this.types[interfaceNode.inherits[0]]; | |||
const supertype: Types.Node = this.types[interfaceNode.inherits[0].name]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was this an unrelated bug?
} | ||
|
||
} else { | ||
// throw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fill in
This PR adds basic generics support to ts2gql.
Example
results in:
Left to do: