Created
January 22, 2020 04:16
-
-
Save atruskie/12abf8cc7979eec14fbee956f13e943c to your computer and use it in GitHub Desktop.
Typescript: strongly typed, lazy evaluated, interpolated strings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let id = (x: number) => x; | |
let param = (x: string) => x; | |
type ParamType<T> = T extends (arg: infer R) => any ? R : never; | |
/** | |
* Templates a string by substituting placeholders for tokens later in execution. | |
* It is designed to work as the tag function for tagged interpolated strings. | |
* @returns A reusable template function that is statically checked for arity and | |
* parameter type compatibility with the placeholders from the interpolated string. | |
* @param strings The strings around the tokens to template | |
* @param placeholders Placeholders that are substituted for token when | |
* templating is done. Note these are transform functions. | |
*/ | |
function makeTemplate<T extends ((any: any) => any)[]>( | |
strings: TemplateStringsArray, | |
...placeholders: T) { | |
// https://github.com/microsoft/TypeScript/issues/12754 should improve this even more | |
return function template(...tokens: { [K in keyof T]: ParamType<T[K]> }) { | |
// interleave the strings with the parameters | |
let result = Array(strings.length + tokens.length); | |
for (let i = 0; i < tokens.length; i++) { | |
result[i * 2] = strings[i]; | |
result[i * 2 + 1] = placeholders[i](tokens[i]); | |
} | |
result[result.length - 1] = strings[strings.length - 1]; | |
return result.join(''); | |
} | |
} | |
// let url: (tokens_0: number, tokens_1: string, tokens_2: number, tokens_3: string) => string | |
let url = makeTemplate`${id}/projects/${param}/sites/${id}/new/${param}` | |
class AudioRecording { id; } | |
let audioRecordingId = (p: AudioRecording) => p.id; | |
// let url2: (tokens_0: AudioRecording, tokens_1: number) => string | |
let url2 = makeTemplate`/audio_recordings/${audioRecordingId}/audio_events/${id}/new/` | |
console.log("final:"); | |
console.log(url); // [Function: template] | |
console.log(url(3, "hello", 2, "world")); // 3/projects/hello/sites/2/new/world | |
//console.log(url(3, "hello", 2, "world", 6)); // error: Expected 4 arguments, but got 5.ts(2554) | |
//console.log(url()); // error: Expected 4 arguments, but got 0.ts(2554) | |
//console.log(url(3, 4, 2, "world")); // error: Argument of type '4' is not assignable to parameter of type 'string'.ts(2345) | |
//console.log(url2(3, "hello")); // error: Argument of type '3' is not assignable to parameter of type 'AudioRecording'.ts(2345) | |
let ar = { id: 12345} as AudioRecording | |
console.log(url2(ar, 6789)); // /audio_recordings/12345/audio_events/6789/new/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment