Home > Enterprise >  Smart strings - like string literals but not and referencing an object - typescript
Smart strings - like string literals but not and referencing an object - typescript

Time:08-18

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 type T. 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 as Post and the param name itself as Param.
  • 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 and Post 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

Typescript playground

  • Related