-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Allow type arguments in generic tagged templates #23430
Changes from 13 commits
b409888
23567ee
60b6d3f
da7967a
090f6bd
7aa916a
4785acb
82e09c9
fe8615d
6107e05
2510c19
eb8eeaf
a2073f1
4bfb1a3
78e98c3
8e27f46
e21a8b8
7f96fec
70feb7b
87bb96d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1032,17 +1032,19 @@ namespace ts { | |
: node; | ||
} | ||
|
||
export function createTaggedTemplate(tag: Expression, template: TemplateLiteral) { | ||
export function createTaggedTemplate(tag: Expression, typeArguments: NodeArray<TypeNode>, template: TemplateLiteral) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the API would be easier to use if the parameter is typed as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call! |
||
const node = <TaggedTemplateExpression>createSynthesizedNode(SyntaxKind.TaggedTemplateExpression); | ||
node.tag = parenthesizeForAccess(tag); | ||
node.typeArguments = typeArguments; | ||
node.template = template; | ||
return node; | ||
} | ||
|
||
export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, template: TemplateLiteral) { | ||
export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, typeArguments: NodeArray<TypeNode>, template: TemplateLiteral) { | ||
return node.tag !== tag | ||
|| node.typeArguments !== typeArguments | ||
|| node.template !== template | ||
? updateNode(createTaggedTemplate(tag, template), node) | ||
? updateNode(createTaggedTemplate(tag, typeArguments, template), node) | ||
: node; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1050,6 +1050,7 @@ declare namespace ts { | |
interface TaggedTemplateExpression extends MemberExpression { | ||
kind: SyntaxKind.TaggedTemplateExpression; | ||
tag: LeftHandSideExpression; | ||
typeArguments?: NodeArray<TypeNode>; | ||
template: TemplateLiteral; | ||
} | ||
type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement; | ||
|
@@ -3516,8 +3517,8 @@ declare namespace ts { | |
function updateCall(node: CallExpression, expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression>): CallExpression; | ||
function createNew(expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression> | undefined): NewExpression; | ||
function updateNew(node: NewExpression, expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression> | undefined): NewExpression; | ||
function createTaggedTemplate(tag: Expression, template: TemplateLiteral): TaggedTemplateExpression; | ||
function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, template: TemplateLiteral): TaggedTemplateExpression; | ||
function createTaggedTemplate(tag: Expression, typeArguments: NodeArray<TypeNode>, template: TemplateLiteral): TaggedTemplateExpression; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is an API breaking change. we need to document it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the other option is to put it at the end, or have multiple overloads. check with @rbuckton There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I wanted to get the conversation rolling on this one. Every time we change the AST, these factory functions get pretty annoying to update. I wonder whether VMs have gotten good at optimizing the "named arguments" pattern for options-bag style APIs. |
||
function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, typeArguments: NodeArray<TypeNode>, template: TemplateLiteral): TaggedTemplateExpression; | ||
function createTypeAssertion(type: TypeNode, expression: Expression): TypeAssertion; | ||
function updateTypeAssertion(node: TypeAssertion, type: TypeNode, expression: Expression): TypeAssertion; | ||
function createParen(expression: Expression): ParenthesizedExpression; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
//// [taggedTemplatesWithTypeArguments1.ts] | ||
declare function f<T>(strs: TemplateStringsArray, ...callbacks: Array<(x: T) => any>): void; | ||
|
||
interface Stuff { | ||
x: number; | ||
y: string; | ||
z: boolean; | ||
} | ||
|
||
export const a = f<Stuff> ` | ||
hello | ||
${stuff => stuff.x} | ||
brave | ||
${stuff => stuff.y} | ||
world | ||
${stuff => stuff.z} | ||
`; | ||
|
||
declare function g<Input, T, U, V>( | ||
strs: TemplateStringsArray, | ||
t: (i: Input) => T, u: (i: Input) => U, v: (i: Input) => V): T | U | V; | ||
|
||
export const b = g<Stuff, number, string, boolean> ` | ||
hello | ||
${stuff => stuff.x} | ||
brave | ||
${stuff => stuff.y} | ||
world | ||
${stuff => stuff.z} | ||
`; | ||
|
||
declare let obj: { | ||
prop: <T>(strs: TemplateStringsArray, x: (input: T) => T) => { | ||
returnedObjProp: T | ||
} | ||
} | ||
|
||
export let c = obj["prop"]<Stuff> `${(input) => ({ ...input })}` | ||
c.returnedObjProp.x; | ||
c.returnedObjProp.y; | ||
c.returnedObjProp.z; | ||
|
||
c = obj.prop<Stuff> `${(input) => ({ ...input })}` | ||
c.returnedObjProp.x; | ||
c.returnedObjProp.y; | ||
c.returnedObjProp.z; | ||
|
||
//// [taggedTemplatesWithTypeArguments1.js] | ||
export const a = f ` | ||
hello | ||
${stuff => stuff.x} | ||
brave | ||
${stuff => stuff.y} | ||
world | ||
${stuff => stuff.z} | ||
`; | ||
export const b = g ` | ||
hello | ||
${stuff => stuff.x} | ||
brave | ||
${stuff => stuff.y} | ||
world | ||
${stuff => stuff.z} | ||
`; | ||
export let c = obj["prop"] `${(input) => ({ ...input })}`; | ||
c.returnedObjProp.x; | ||
c.returnedObjProp.y; | ||
c.returnedObjProp.z; | ||
c = obj.prop `${(input) => ({ ...input })}`; | ||
c.returnedObjProp.x; | ||
c.returnedObjProp.y; | ||
c.returnedObjProp.z; |
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.
I'm pretty sure this duplicates the logic in the
else if
below, no? You should just need to add to theCallExpression
cast below. (I'd add aCallLikeExpressionWithTypeArguments
, that'sCallLikeExpression
sans decorators, which are the only one without them)