I have a interface column.
interface column {
field: string
label: string
}
So a row of columns would be:
const cols = [{
label: 'First Name',
field: 'fname',
},
{
label: 'Last Name',
field: 'lname',
},
{
label: 'Email',
field: 'email',
}]
Data would be an array of data rows.
const data:{}[] = [{
fname: 'bob', lname: 'dylan', email: 'db@email.com',
fname: 'van', lname: 'halen', email: 'vh@email.com'
}]
Is there a way to enforce keys ie [fname, lname, email] of data to be the values of corresponding cols[n]['field']?? Thanks in advance.
Yes, you can do this as long as you define cols
in such a way so that the compiler doesn't widen the field
properties to string
. The easiest way to do that with TS3.4+ is to use a const
assertion :
const cols = [{
label: 'First Name',
field: 'fname',
},
{
label: 'Last Name',
field: 'lname',
},
{
label: 'Email',
field: 'email',
}] as const;
That as const
means that cols
will be considered a readonly tuple where the label
and field
properties have string literal types.
From there you can make a type alias that converts a set of Column
-compatible values to an object type with the field
properties as keys. Of course there's no mention of what the value type of each field should be, so I'm assuming it's always string
:
interface Column {
field: string
label: string
};
type StringObjFromColumns<T extends readonly Column[]> =
{ [K in T[number]["field"]]: string }
And then we can define your data type as StringObjFromColumns<typeof cols>
:
const data: StringObjFromColumns<typeof cols>[] = [{
fname: 'bob', lname: 'dylan', email: 'db@email.com',
}, {
fname: 'van', lname: 'halen', email: 'vh@email.com'
}, {
fname: 'van', lname: 'morrison', age: 75, email: 'vm@example.com' // error! age is extra
}, {
fname: 'ted', lname: 'nugent' // error! email is missing
}]
You can see how it enforces the constraint that each object must have those three fields of type string
.
Okay, hope that helps; good luck!
This is not possible as typescript types are enforced at compile time and not run time. See my answer to this question which answers yours.
This is a tricky one.
Having
interface Column {
field: string
label: string
}
We can define a RowObject
and, additionally, just for the sake of example, a Row
which has the column
property and the row
itself.
interface RowObject {
[key: string]: string;
}
interface Row {
cols: Column[];
object: RowObject;
}
Now, we should define a function to prototype the new objects.
const createRowBlueprint = (cols: Column[]): Row => {
return {
cols: [...cols],
object: cols.map(c => c.field)
.reduce((prop: RowObject, prev: string) => (prop[prev] = '', prop), {})
};
};
I just defined the Row
interface to dynamically access the "real row object" properties, like so
const row = createRowBlueprint(cols);
row.cols.forEach(col => {
const { field } = col;
const value = row.object[field];
console.log(`Field ${field} binded to ${value}`);
});
Given your columns
collection, this outputs the following
{
cols: [
{ label: 'First Name', field: 'fname' },
{ label: 'Last Name', field: 'lname' },
{ label: 'Email', field: 'email' }
],
row: { fname: '', lname: '', email: '' }
}
Field fname binded to
Field lname binded to
Field email binded to
Obviously, I set the default value to an empty string. But, the prove that it works is that the value accessed from the field is not undefined
.
Finally, this approach is not safety typed. But, as far as I know, Typescript does not support dynamic type definition in compilaiton time, for the moment... So this is a tricky workaround.
Hope it helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.