microsoft / microsoft/TypeScript
[feature request] Support Custom Types for Tagged Template Expressions
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
Problem Statement
It is common to embed queries in tagged template expressions within typescript. For example, you might use sql:
const result = await querySQL(sql`SELECT * FROM users WHERE id = ${userID}`);
You also see a similar pattern with graphql:
const result = await queryGraph(graphql`query { user { id, name } }`);
Currently, there is no way to provide type information about what is returned by queryGraph/querySQL. A lot of potential type information is lost at the boundary here.
I obviously don't expect typescript to read the graphql or SQL schema, as that's way out of scope. What I would like is a way to provide this information as a module author.
Proposal
I believe that all that's needed is a way to write plugins that answer the question "given a tagged template expression, what type does it return". This would only affect the type checker. It would not change generated code or add any new syntax.
A new compilerConfig option called taggedTemplateHandlers would be added, that would take an array of paths to typescript files. An example might look something like:
import {parseSqlQuery, convertSqlTypeToTypeScriptType} from 'sql-helpers';
import * as ts_module from "typescript/lib/tsserverlibrary";
export default {
tag: 'sql',
templateHandler(modules: {typescript: typeof ts_module}, strings: string[], ...expressions: Array<ts_module.Expression>) {
const ts = modules.typescript;
const fields = parseSqlQuery(strings.join('"EXPRESSION"'));
return ts.createTypeReferenceNode(
ts.createIdentifier('SqlQuery'), // typeName
ts.createNodeArray([ // typeArguments
ts.createTypeLiteralNode(Object.keys(fields).map(field => {
return ts.createPropertySignature(
ts.createIdentifier(field),
undefined, // question token
convertSqlTypeToTypeScriptType(fields[field]), // type
undefined, // initializer
)
}))
])
);
}
};
You could then define querySQL like:
define function querySQL<TResult>(query: SqlQuery<TResult>): Promise<TResult>;
Language Feature Checklist
- Syntactic - no new changes
- Semantic
- When a tagged template is encountered by the type-checker
- See if a
taggedTemplateHandlershas been registered for that tag - Call that
taggedTemplateHandlerif one exists. - Use the
TypeNodereturned by thetaggedTemplateHandlerin place of the default behaviour.
- See if a
- When a tagged template is encountered by the type-checker
- Emit - no new changes
- Compatibility - no new syntax is added/changed, so it should be fully backwards/forwards compatible.
- Other
- I expect there will be some performance impact, but hopefully the fact that both plugins and the compiler are written in typescript should make this minimal.
- Ideally, this information would be used for autocomplete helpers as well as in the typechecker, I do not know if that would require extra work.
I'm happy to do my best to help implement this, but I would need some pointers on where to start.
P.S. would it be possible to pass in the type of the expressions, in place of the actual expressions themselves?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue names no files, tests, or entry points. Start by locating tagged-template type checking and compilerConfig handling, then determine how a handler could provide a returned type without affecting emit; done would mean implementing and testing the proposed configuration and type-checker behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100