To maintain literal types when inferring the type of a constant, you can utilize the as const
assertion:
const data = { x: 1, y: '2' } as const
// Equivalent to
// const data: {
// readonly x: 1;
// readonly y: "2";
// }
By using this, the object becomes read-only, but for a constant, this is usually acceptable.
If you prefer properties not to be readonly
, there are various approaches available, although none are particularly seamless.
One approach is applying as const
on the literals:
const data = { x: 1 as const, y: '2' as const }
// Equivalent to
// const data: {
// x: 1;
// y: "2";
// }
Alternatively, declare the readonly
version initially and then convert it to mutable:
type Mutable<T> = { -readonly [P in keyof T]: T[P]}
const ro_data = { x: 1 , y: '2' } as const
const data: Mutable<typeof ro_data> = ro_data
// const data: {
// x: 1;
// y: "2";
// }
Or another option is using a function to infer string literal types:
function asLiterals<T extends Record<string, string | number | null | undefined | boolean >>(o: T) {
return o;
}
const data = asLiterals({ x: 1 , y: '2' });
// Equivalent to
// const data: {
// x: number;
// y: string;
// }