I currently have a type (FieldTypeProperties) that I want to enforce dynamic type checking when it's keys are passed to a function or object:
export enum supportedFields{
Text = "Text",
Note = "Note",
Number = "Number"
}
export type FieldTypeProperties = {
[supportedFields.Text]: TypeA;
[supportedFields.Note]: TypeB;
[supportedFields.Number]: TypeC;
};
I've managed to define a function that behaves as expected:
const createField = <K extends keyof FieldTypeProperties>(fieldType: K, properties: FieldTypeProperties[K]): IResult => {
let field: IResult;
//Some code
return field;
}
The usage works as expected and the fieldType argument value does check for the correct Type in the properties argument:
let test = createField(supportedFields.Note, {});
// Checks for Type B in the properties argument, as defined in the FieldTypeProperties type
However for batch operations, I want to loop an array of objects that conform to the arguments in createField(), so:
type BatchObj<K extends keyof FieldTypeProperties> = {
fieldType: K;
properties: FieldTypeProperties[K];
}
// Intended Usage:
let testBatch1: BatchObj<keyof FieldTypeProperties>[] = [
{
fieldType: supportedFields.Text,
properties: {}, //Type A should only be checked, but it's not, instead a union of TypeA | TypeB | TypeC is checked
},
]
// Usage that works but is NOT desired:
let testBatch2: BatchObj<supportedFields.Text>[] = [
{
fieldType: supportedFields.Text,
properties: {}, //Type A is checked, but now any additional objects added to this array must have a fieldType = supportedFields.Text and properties = TypeA
}
]
I can understand why this is happening, since in testBatch1: fieldType (K) would the set of keys for the FieldTypeProperties type, and properties (FieldTypeProperties[K]) would be the set of values for the FieldTypeProperties type.
Is there a way to achieve my desired usage, based on what is inferred in the code snippet above? Or is this a typescript limitation and as it stands is not a possibility?
Your responses will be greatly appreciated :)
CodePudding user response:
You can generate a union for each possible key : BatchObj<"Text"> | BatchObj<"Note"> | BatchObj<"Number">
using the distribution of conditional types. This will ensure the relationship between fieldType
and properties
is preserved:
type BatchObj<K extends keyof FieldTypeProperties> = {
fieldType: K;
properties: FieldTypeProperties[K];
}
type BatchObjUnion<K extends keyof FieldTypeProperties = keyof FieldTypeProperties> = K extends K ? BatchObj<K>: never
// Intended Usage:
let testBatch1: BatchObjUnion[] = [
{
fieldType: supportedFields.Text,
properties: { text: "" }, //Type A
},
]