我有一個 polymorficFactory 函式,它將根據提供的“鑒別器”屬性實體化類:
type ClassConstructor<T> = {
new (...args: any[]): T;
};
type ClassMap<T> = Record<string, ClassConstructor<T>>;
function polymorficFactory<T extends object>(
classMap: ClassMap<T>,
discriminator: string,
input: Record<string, any>,
): T {
if (!input[discriminator]) throw new Error('Input does not have a discriminator property');
const discriminatorValue = input[discriminator];
const constructor = classMap[discriminatorValue];
return plainToInstance(constructor, input); // class-transformer util
}
它用于來自請求有效負載的未知物件:
const MOCK_PAYLOAD = {
type: 'SOME_DTO',
someProperty: 1,
someStr: 'Lorem',
someNestedProp: {
someBool: true,
}
} as as Record<string, unknown>; // Record<string, unknown/any>, since comes from JSON payload, but will always be an object
const dto = polymorficFactory<SomeDto | SomeOtherDto>(
{
SOME_DTO: SomeDto,
SOME_OTHER_DTO: SomeOtherDto,
},
'type',
MOCK_PAYLOAD ,
);
dto; // when hovering, type will be "SomeDto | SomeOtherDto"
如果我不提供型別引數:
const dto = polymorficFactory(
...
);
dto; // when hovering, type will be "SomeDto"
它選擇在此物件中找到的第一個值:
{
SOME_DTO: SomeDto,
SOME_OTHER_DTO: SomeOtherDto,
}
有沒有一種方法可以讓 tsSomeDto | SomeOtherDto從映射值推斷聯合型別或在提供的 classMap 中找到的任何其他 Dto 類,而無需顯式提供它?
即,對于以下classMap:
{
A: aDto,
B: bDto,
C: cDto,
}
我希望為結果推斷出以下聯合型別:
aDto | bDto | cDto
游樂場鏈接
uj5u.com熱心網友回復:
您需要對函式引數進行一些推理。經驗法則:如果你想推斷一個值,你應該重放所有在型別范圍內的運行時完成的轉換。
我的意思是如果你構建一個類實體,你需要呼叫InstanceType內置型別來獲取類實體型別。
這些類映射值得制作不可變或換句話說使用as const斷言。
考慮一下:
import { plainToInstance } from "class-transformer";
type ClassConstructor<T> = {
new(...args: any[]): T;
};
function polymorficFactory<
Inst,
Values extends PropertyKey,
Discriminator extends string,
ClassMap extends Record<Values, ClassConstructor<Inst>>,
Input extends Record<Discriminator, Values>
>(
classMap: ClassMap,
discriminator: Discriminator,
input: Input,
): InstanceType<ClassMap[Input[Discriminator]]>
function polymorficFactory<
Inst,
Values extends PropertyKey,
Discriminator extends string,
ClassMap extends Record<Values, ClassConstructor<Inst>>,
Input extends Record<Discriminator, Values>
>(
classMap: ClassMap,
discriminator: Discriminator,
input: Input,
) {
if (!input) throw new Error('Input is not an object');
if (!input[discriminator]) throw new Error('Input does not have a discriminator property');
const discriminatorValue = input[discriminator];
const klass = classMap[discriminatorValue];
const result = plainToInstance(klass, input)
return result
}
class SomeDto {
tag = 'SomeDto'
}
class SomeOtherDto {
tag = 'SomeOtherDto'
}
const CLASS_MAP = {
SOME_DTO: SomeDto,
SOME_OTHER_DTO: SomeOtherDto,
} as const
const MOCK = {
type: 'SOME_OTHER_DTO',
someProperty: 1,
} as const
const explicitParams = polymorficFactory(
CLASS_MAP,
'type',
MOCK,
);
explicitParams; // when hovering, type will be " SomeOtherDto"
操場
正如您可能已經注意到的那樣,每個值都polymorficFactory被正確推斷。
- 看到這條線
const discriminatorValue = input[discriminator];。如果您希望discriminator成為 的有效索引input,則應應用適當的約束:
function polymorficFactory<
// .....
Values extends PropertyKey,
Discriminator extends string,
Input extends Record<Discriminator, Values>
>(
// ...
discriminator: Discriminator,
input: Input,
)
input變數應該擴展Record<Discriminator, Values>。這意味著這discriminator是允許的索引input。
考慮這條線const klass = classMap[discriminatorValue];
discriminatorValue這input[discriminator]應該是 的有效鍵classMap,因此,我們需要在此處應用適當的約束:
function polymorficFactory<
Inst,
Values extends PropertyKey,
Discriminator extends string,
ClassMap extends Record<Values, ClassConstructor<Inst>>,
Input extends Record<Discriminator, Values>
>(
classMap: ClassMap,
discriminator: Discriminator,
input: Input,
)
參見Values泛型,它起到valueinInput和 key in的作用ClassMap。這很重要。
所以現在,當所有引數都被推斷出來時,我們需要推斷一個回傳型別。我們的回傳型別是 的一個實體classMap[input[discriminator]],或者type換句話說:InstanceType<ClassMap[Input[Discriminator]]>
如果您對函式引數推斷的更多解釋感興趣,可以查看我的文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/529771.html
