使用泛型定义 return 函数的值,并将可区分的联合作为输入
Use generics to define return value for function with discriminated union as input
我想创建一个函数来接收具有 updatedAt
and/or createdAt
属性(作为日期)和 returns 相同对象但具有序列化为字符串的单个或两个值。
首先,如何定义这个函数的return类型?
其次,我觉得使用泛型更好,但我还没有找到正确的方法。
这是我目前拥有的:
type HasBothValues = {
[key: string]: any;
createdAt: Date;
updatedAt: Date;
};
type HasCreatedAt = {
[key: string]: any;
createdAt: Date;
};
type HasUpdatedAt = {
[key: string]: any;
updatedAt: Date;
};
type AllInputVariants = HasBothValues | HasCreatedAt | HasUpdatedAt;
const serializeDates = (obj: AllInputVariants): any => { // <-- Don't like the any return type!
const retObj = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return obj;
};
export { serializeDates };
感谢任何提示!
函数重载似乎是一个很好的解决方案。
首先我们创建一个名为 Overwrite
的类型来更改某些属性的类型。
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U extends infer O ? {
[K in keyof O]: O[K]
} : never
现在我们可以添加所有可能的函数重载:
function serializeDates<T extends HasBothValues>(obj: T): Overwrite<T, {updatedAt: string, createdAt: string}>
function serializeDates<T extends HasUpdatedAt>(obj: T): Overwrite<T, {updatedAt: string}>
function serializeDates<T extends HasCreatedAt>(obj: T): Overwrite<T, {createdAt: string}>
function serializeDates<T extends AllInputVariants>(obj: T) {
const retObj: any = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return retObj;
};
让我们看看它是否有效:
const t1 = serializeDates({
createdAt: new Date(),
})
// const t1: {
// createdAt: string;
// }
const t2 = serializeDates({
updatedAt: new Date()
})
// const t2: {
// updatedAt: string;
// }
const t3 = serializeDates({
createdAt: new Date(),
updatedAt: new Date()
})
// const t3: {
// updatedAt: string;
// createdAt: string;
// }
const t4 = serializeDates({
createdAt: new Date(),
updatedAt: new Date(),
a: 123,
b: "123"
})
// const t4: {
// a: number;
// b: string;
// updatedAt: string;
// createdAt: string;
// }
再想想,我们可以做得更好。让我们修改 Overwrite
以仅覆盖实际存在于 T
.
上的 T
的属性
type Overwrite<T, U> = (Pick<T, Exclude<keyof T, keyof U>> & Pick<U, Extract<keyof U, keyof T>>) extends infer O ? {
[K in keyof O]: O[K]
} : never
现在我们不再需要函数重载了:
function serializeDates<
T extends { updatedAt?: Date, createdAt?: Date }
>(obj: T): Overwrite<T, {updatedAt: string, createdAt: string}> {
const retObj: any = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return retObj as any;
};
我想创建一个函数来接收具有 updatedAt
and/or createdAt
属性(作为日期)和 returns 相同对象但具有序列化为字符串的单个或两个值。
首先,如何定义这个函数的return类型?
其次,我觉得使用泛型更好,但我还没有找到正确的方法。
这是我目前拥有的:
type HasBothValues = {
[key: string]: any;
createdAt: Date;
updatedAt: Date;
};
type HasCreatedAt = {
[key: string]: any;
createdAt: Date;
};
type HasUpdatedAt = {
[key: string]: any;
updatedAt: Date;
};
type AllInputVariants = HasBothValues | HasCreatedAt | HasUpdatedAt;
const serializeDates = (obj: AllInputVariants): any => { // <-- Don't like the any return type!
const retObj = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return obj;
};
export { serializeDates };
感谢任何提示!
函数重载似乎是一个很好的解决方案。
首先我们创建一个名为 Overwrite
的类型来更改某些属性的类型。
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U extends infer O ? {
[K in keyof O]: O[K]
} : never
现在我们可以添加所有可能的函数重载:
function serializeDates<T extends HasBothValues>(obj: T): Overwrite<T, {updatedAt: string, createdAt: string}>
function serializeDates<T extends HasUpdatedAt>(obj: T): Overwrite<T, {updatedAt: string}>
function serializeDates<T extends HasCreatedAt>(obj: T): Overwrite<T, {createdAt: string}>
function serializeDates<T extends AllInputVariants>(obj: T) {
const retObj: any = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return retObj;
};
让我们看看它是否有效:
const t1 = serializeDates({
createdAt: new Date(),
})
// const t1: {
// createdAt: string;
// }
const t2 = serializeDates({
updatedAt: new Date()
})
// const t2: {
// updatedAt: string;
// }
const t3 = serializeDates({
createdAt: new Date(),
updatedAt: new Date()
})
// const t3: {
// updatedAt: string;
// createdAt: string;
// }
const t4 = serializeDates({
createdAt: new Date(),
updatedAt: new Date(),
a: 123,
b: "123"
})
// const t4: {
// a: number;
// b: string;
// updatedAt: string;
// createdAt: string;
// }
再想想,我们可以做得更好。让我们修改 Overwrite
以仅覆盖实际存在于 T
.
T
的属性
type Overwrite<T, U> = (Pick<T, Exclude<keyof T, keyof U>> & Pick<U, Extract<keyof U, keyof T>>) extends infer O ? {
[K in keyof O]: O[K]
} : never
现在我们不再需要函数重载了:
function serializeDates<
T extends { updatedAt?: Date, createdAt?: Date }
>(obj: T): Overwrite<T, {updatedAt: string, createdAt: string}> {
const retObj: any = { ...obj };
if (obj.createdAt) {
retObj.createdAt = obj.createdAt.toISOString();
}
if (obj.updatedAt) {
retObj.updatedAt = obj.updatedAt.toISOString();
}
return retObj as any;
};