I'm most like not asking Google the correct question, so maybe someone who has done something similar can point me in the correct direction?
Let's example with the basic bit of code:
const var1 = "abc";
console.log(`var is: ${var1}`);
If you were to incorrectly reference the variable in the string literal, you'd get an error.
What I am trying to do is similar but for a function/method.
public format(message: string, meta: object) {
// do blah here to format
}
format("this var: {var1}", {
var1: "abc"
});
I want to have VSCode highlight the var1
reference from the passed in object dynamically (however that isn't the main thing).
I need to be able to have typescript validate the object and string similar to that of a string literal on compilation.
So if you were to mistype the reference {variable}
in the message, but not define a the variable in the meta object, it would error out.
Thanks
CodePudding user response:
Typescript template string literal types are so cool.
You can parse the string literal type for params, and use that to enforce your types.
type ParamsFromString<T extends string> =
T extends `${ infer Pre }{${ infer Param }}${ infer Post }`
? Param | ParamsFromString<`${Pre}${Post}`>
: never
- Line 1: This type accepts a
string
typeT
. This is the format string literal type. - Line 2: This checks if the string matches the format that contains a param. If it does, then infer the part before the match as
Pre
, the part after asPost
and the param name itself asParam
. - Line 3: This does two things. First, it returns the param name found. And second, it looks for more params. But to avoid finding the same param again, we make a new string literal that is just the
Pre
andPost
concatenated. Then we return the param we found and all other params as a union with|
. - Line 4: Either the format string has no params, or we already found them all, so return
never
since there is nothing to find.
Testing that out:
type Test = ParamsFromString<'foo {bar} {baz}'>
// "bar" | "baz"
You can then use that type in a function like so:
function format<T extends string>(
message: T,
meta: Record<ParamsFromString<T>, string>
) {
// do blah here to format
}
Here the format string is accepted as a string literal type as T
. Then the object passed in must have keys that are the params derived from that string.
Proof:
format("this var: {var1}", { var1: "abc" }); // fine
format("this var: {var1}", { bad: "abc" }); // type error