我想创建一个函数,该函数接收一个具有updatedAt和/或createdAt个属性(作为日期)的对象,并返回同一个对象,但将单个或两个值序列化为字符串.

首先,我如何定义这个函数的返回类型?

我还没有找到一种更好的方法来处理泛型.

这就是我目前拥有的:

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;
};

Playground


让我们看看它是否有效:

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;
};

Playground

Typescript相关问答推荐

为什么在TypScript中写入typeof someArray[number]有效?

TypeScript泛型参数抛出错误—?&#"' 39;预期"

如果一个变量不是never类型,我如何创建编译器错误?

向NextAuth会话添加除名称、图像和ID之外的更多详细信息

PrimeNG日历需要找到覆盖默认Enter键行为的方法

Select 下拉菜单的占位符不起作用

记录键的泛型类型

ANGLING v17 CLI默认设置为独立:延迟加载是否仍需要@NgModule进行路由?

推断从其他类型派生的类型

TypeScrip错误:为循环中的对象属性赋值

类型脚本-在调用期间解析函数参数类型

如何使用泛型函数参数定义类型脚本函数

如何为特定参数定义子路由?

KeyOf关键字在特定示例中是如何工作的

带有';的类型脚本嵌套对象可选属性错误满足';

窄SomeType与SomeType[]

使用 Next.js 路由重定向到原始 URL 不起作用

如何解决 Angular 中的 ts(18048) 错误

该表达式不可调用,联合体的每个成员

使用useEffect和useCallback是多余的吗?