I would like to improve the type-checking of the following object:
interface Config {
fields: Record<string, unknown>;
table: { id: string }[];
}
const config: Config = {
fields: {
id: {}
},
table: [
{ id: 'id' },
{ id: 'name' },
]
};
The part I'm struggling with is that I want to type-check that the table
object id
should match a key in fields
. If table > id
is not present in fields
keys, then it should error. So:
{ id: 'id' }
is OK, sinceid
key exists infields
{ id: 'name' }
should error,name
key doesn't exist infields
Is this possible? Plan B is to do this check at runtime.
CodePudding user response:
It is not possible (yet) to create a standalone type
which can do validation like that. But generic type
s are powerful enough to achieve this. Generic types for validation are best used in combination with a function.
function useConfig<F>(config: { fields: F, table: { id: keyof F }[]}) {}
In this function we have the generic type F
which holds information about the object we pass with the fields
property. We can then use keyof F
inside table
.
Some tests to validate the result:
useConfig({
fields: {
id: {}
},
table: [
{ id: 'id' }
]
})
// works
useConfig({
fields: {
id: {}
},
table: [
{ id: 'id' },
{ id: 'name' }
]
})
// error
useConfig({
fields: {
id: {},
name: {}
},
table: [
{ id: 'id' },
{ id: 'name' }
]
})
// works
CodePudding user response:
type fieldsType = {id:any}
const config:{fields:fieldsType, table:{[k:string]:keyof fieldsType}[]} = {
fields: {
id: {},
},
table: [
{ id: 'id' },
{ id: 'name' },
]
};
CodePudding user response:
Something like this could work, although I don't like the explicit list of fields on the generic… it would be more convenient if there was some kind of type-magic for this. ^_^U
interface Config<T extends string> {
fields: {
[P in T]: unknown
};
table: { id: T }[];
}
const config: Config<'id' | 'title'> = {
fields: {
id: {},
title: {},
},
table: [
{ id: 'id' },
{ id: 'name' }, // <== this errors
]
};