Home > Net >  Construct type information from string via template literal
Construct type information from string via template literal

Time:05-04

Is it possible to construct some type directly from the string provided?

I want to create a type something like shown below:

type MyConfiguration<T> = {
  items: T[];
  onChange: (updated: ConstructedType<T>) => void;
}

const conf: MyConfiguration = {
  items: ['id', 'nested.id', 'nested.name'],
  onChange: updated => {
    console.log(`You updated ${updated.nested.name}(id: ${updated.nested.id})`);
  },
};

So it will generate a type for updated to be {id: string; nested: { id: string; name: string}}

CodePudding user response:

This solution might not be perfect but the type of updated seems to be correct:

type First<T extends string> = T extends `${infer L}.${string}` ? L : T
type Nested<T extends string> = T extends `${string}.${infer R}` ? R : string

type _ConstructedType<T extends string> = string extends Nested<T> ? string : {
  [Key in T as First<Nested<T>>]: _ConstructedType<Nested<T>>
}

type ConstructedType<K extends readonly string[]> = {
  [Key in K[number] as First<Key>]: _ConstructedType<Key>
}

function createConf<K extends readonly string[]>(conf: {items: K, onChange: (updated: ConstructedType<K>) => any}) {
    return conf
}

createConf({
  items: ['id', 'nested.id', 'nested.name'] as const,
  onChange: updated => {
    console.log(`You updated ${updated.nested.name}(id: ${updated.nested.id})`);
  },
})

In your question you specified that you want to have a type called MyConfiguration. A type alone cannot enforce any constraints between properties. So we have to create a factory function called createConf. We can now pass a conf object to createConf and all types are inferred.

A drawback that I don't know how to fix yet, is that you have to write as const behind the items array. Otherwise TypeScript will infer the type as string[] and not as a tuple.

Playground

  • Related