I would like to get type inference of dynamics objects. I searched through the site but didn't find exactly what i was looking for. I am sorry if it is repeated.
Basically I want to have some global constants objects of the same shape, so that I switch which is active but still being able to have the same properties inside, but only with different values.
const example = {
constant1: {
value1: 'value1',
value2: 'value2',
value3: 'value3',
},
constant2: {
value1: 'other value1',
value2: 'other value2',
value3: 'other value3',
},
constant3: {
value1: 'another value1',
value2: 'another value2',
value3: 'another value3',
},
};
So the example object can have any property, but they have to be equal, if I change one of the values inside one of the constants, I need it to throw an type error, but if I change in everyone of them, it's fine.
This is how I tried at first, it seemed to work but not how i would like it.
interface TypeTest<T> {
[key: string]: T;
}
const CreateTest = <T>(obj: TypeTest<T>) => obj;
const test = CreateTest({
some: {
other: 'test',
},
thing: {
other: 'testing',
},
});
type constants = keyof typeof test;
As interfaces don't get type inference, I am using a creator function that receives the object and returns it typed. But it don't type the inner objects, as typescript infer a union type of each inner object. Also I don't get any type of the outer object, as
type constants = string | number
To work around this, I tried it this way:
type TypeTest<D, T> = {
[key in keyof D]: T;
};
const CreateTest = <D, T>(obj: TypeTest<D, T>) => obj;
const test = CreateTest({
some: {
other: 'test',
},
thing: {
other: 'testing',
},
});
type constants = keyof typeof test;
Now I get the right type of the outer object(type constants = "some" | "thing"
), but completely lost type inference of the inner object, as now I only the unknown type, even if if I use [key2 in keyof T]: string
as the shape of the inner object, it still is unknown.
I was able to work around all this, but in the end the developer experience is not that great.
interface TypeTest<T extends readonly string[], U extends readonly string[]> {
outer: T;
inner: U;
values: {
[Property1 in T[number]]: {
[Property2 in U[number]]: string;
};
};
}
const CreateTest
= <
T extends readonly string[],
U extends readonly string[],
> (constants: TypeTest<T, U>) => constants.values;
const test = CreateTest({
outer: [ 'some', 'thing' ] as const,
inner: [ 'other' ] as const,
values: {
some: {
other: 'test',
},
thing: {
other: 'testing',
},
},
});
type constants = keyof typeof test;
type values = keyof typeof test[constants];
Now I pass two arrays that are only used to get the types and thrown away after, also both arrays have to be asserted as const, otherwise I only get string[]
type out of them.
So, is there a way to pass an object to the CreateTest()
function, and get the typed value out of it? With
type constants = "some" | "thing"
and
type values = "other
EDIT:
The level of nesting is only 2 levels deep. But is it possible to get more of it?
Also is it possible to get different level of nesting? Like one object having 3 levels of nesting while other having 5.
I won't be needing it that way, I am just curious.