1

I have a configuration object as follows:

const config = { envs: ['dev', 'test', 'prod'], targets: ['> 2%'] };

Currently, the typescript compiler infers the type of this object to be:

type IConfig = { envs: string[], targets: string[] };

Which makes sense as I might mutate this array at some point after declaration.

I'm not going to change it though, so I'd like it to be:

type IConfig = { envs: ['dev', 'test', 'prod'], targets: ['> 2%'] };

Is there any way to tell the compiler to infer the type of config.envs as a tuple type with string literals (without typing it out)?

Edit: The best answer is 90% of the way there, but I'm hoping for a method that can be applied to the object as a whole rather than each of the properties. I've added another property to the examples to make this clearer.

Christian Scott
  • 893
  • 7
  • 19

1 Answers1

2

Combining the standard trick to infer literal types with a trick I just learned to infer a tuple type rather than an array type:

function asTupleOfLiterals<T extends string, U extends [T, ...T[]]>(tuple: U): U {
    return tuple;
}
const config = { envs: asTupleOfLiterals(['dev', 'test', 'prod']) };

Round 2: applying to an object with multiple tuple-valued properties

Miraculously, if we just wrap [T, ...T[]] in an object with an index signature, the contextual typing seems to work:

function asObjectOfTuplesOfLiterals<T extends string,
    U extends {[n: string]: [T, ...T[]]}>(obj: U): U { return obj; }

const config = asObjectOfTuplesOfLiterals(
    { envs: ['dev', 'test', 'prod'], targets: ['> 2%'] });

FTR, there's an open suggestion to make it easier to infer literal types and one to make it easier to infer tuple types.

Matt McCutchen
  • 28,856
  • 2
  • 68
  • 75
  • 3
    Can be simplified a [bit](https://www.typescriptlang.org/play/index.html#src=function%20asTupleOfLiterals%3CT%20extends%20string%5B%5D%3E(...args%3A%20T)%3A%20T%20%7B%0D%0A%20%20%20%20return%20args%3B%0D%0A%7D%0D%0Aconst%20config%20%3D%20%7B%20envs%3A%20asTupleOfLiterals('dev'%2C%20'test'%2C%20'prod')%20%7D%3B) – Aleksey L. Sep 24 '18 at 04:41
  • These are super useful - thanks to both of you. I don't think I explained this well enough, but I'm hoping for a method that can be applied to the object as a whole rather than to each of the properties. I've added another property to my examples to make this clearer. The properties will always be arrays of strings. Apologies! – Christian Scott Sep 24 '18 at 04:50