I'm trying to create a number of types to be able to write:
- MyObjectDetails — defines MyObject structure
- MyObject - object that correctly follows these MyObjectDetails.
// Details example
const testObjectDetails: MyObjectDetails = {
name: 'Test',
fields: [
{
name: 'property1'
},
{
name: 'property2'
}
]
}
// Object example
const testObject: MyObject<typeof testObjectDetails> = {
property1: 123,
property3: 456 // Should throw Error -> no such field in the details
// Should throw Error -> property2 is missing
}
I wrote some code, but they it's not working as expected:
type MyField = string extends infer T ? Readonly<{
name: T
}> : never
type MyObjectDetails = Readonly<{
name: string,
fields: ReadonlyArray<MyField>
}>
type MyObject<T extends MyObjectDetails> = {
[K in T['fields'][number]['name']]: any;
}
Can you help me?
CodePudding user response:
The problem here is the declaration of testObjectDetails
. You are declaring it with the type MyObjectDetails
. When you do this, all type information about specific properties is lost because MyObjectDetails
does not contain details about specific property names.
One way to solve this would be to use as const
and remove the type MyObjectDetails
.
const testObjectDetails = {
name: 'Test',
fields: [
{
name: 'property1'
},
{
name: 'property2'
}
]
} as const
If you still want to have type safety when declaring the testObjectDetails
object, you need to use a factory function:
const testObjectDetails = createTestObjectDetails({
name: 'Test',
fields: [
{
name: 'property1'
},
{
name: 'property2'
}
]
} as const)
function createTestObjectDetails<T extends MyObjectDetails>(testObjDetails: T) : T{
return testObjDetails
}