简体   繁体   English

在 TypeScript 中实现一个 object 的泛型

[英]Implement a generic that is an object in TypeScript

I want to have a onFilterChange helper function that can be used for all filters, so that I dont need to type it for every different filter, but I'm stuck:我想要一个可用于所有过滤器的 onFilterChange 帮助器 function,这样我就不需要为每个不同的过滤器键入它,但我被卡住了:

// helper.ts
export function onFilterChange(prevState: Record<string, any>, fieldName: string, value: string, isDelete = false): Record<string, any> {
  const newFilters: Record<string, any> = { ...prevState };

  if (isDelete) {
    delete newFilters[fieldName];
  } else {
    newFilters[fieldName] = value;
  }

  if (fieldName !== 'page' && 'page' in newFilters) {
    newFilters.page = '1';
  }

  return newFilters;
}

// index.tsx
const [filters, setFilters] = React.useState({page: 1, order: 'desc'});
setFilters(prevState => onFilterChange(prevState, 'statuses', '', true)); // error

The error for the last line of code above is: Type 'Record<string, any>' is missing the following properties from type '{ page: number;上面最后一行代码的错误是:Type 'Record<string, any>' is missing the following properties from type '{ page: number; order: string;命令:字符串; }': page, order }':页面,顺序

I've also tried T extends Record<string, any> but there is error for this code:我也尝试过 T extends Record<string, any> 但此代码有错误:

newFilters[fieldName] = value;

Type string cannot be used to index type T.类型字符串不能用于索引类型 T。

Appreciate any pointers.感谢任何指针。 Thanks谢谢

You can apply a type assertion like this:您可以像这样应用类型断言:

// index.tsx
const [filters, setFilters] = React.useState({page: 1, order: 'desc'});
setFilters(prevState => onFilterChange(prevState, 'statuses', '', true) as typeof prevState); // Ok

The problem with the this answer is that if you assert the type, you will not be able to access the value of filters.statuses later.这个答案的问题是,如果您断言类型,您以后将无法访问filters.statuses的值。

You'll have to type the useState ahead of time你必须提前输入 useState

const [filters, setFilters] = React.useState<
  {page: number; order: string; statuses: string}
>({page: 1, order: 'desc'});

And change your function to a generic并将您的 function 更改为通用

//Note this generic only extends page, since this is the only
//value we need guranteed, and access directly with the line below
export function onFilterChange<T extends { page: string }>(
  prevState: T,
  fieldName: keyof T,
  value: T[typeof fieldName],
  isDelete = false
): T {
  const newFilters: T = { ...prevState };

  if (isDelete) {
    delete newFilters[fieldName];
  } else {
    newFilters[fieldName] = value;
  }

  if (fieldName !== 'page' && 'page' in newFilters) {
    newFilters.page = '1'; //Here is why T needs to extend {page: string}
  }

  return newFilters;
}

Alternatively, if you are setting lots of fields but don't know their type yet you can use the index accessor或者,如果您要设置很多字段但不知道它们的类型,您可以使用索引访问器

const [filters, setFilters] = React.useState<{
  page: number;
  order: string;
} & { [index: string]: string }>({page: 1, order: 'desc'});

We have to use an intersection here, otherwise, we'll get an error Property 'page' of type 'number' is not assignable to 'string' index type 'string'.ts(2411) .我们必须在这里使用交集,否则,我们会得到一个错误Property 'page' of type 'number' is not assignable to 'string' index type 'string'.ts(2411)

Also note that TS catches an error here you already made, where the page value is a number in your useState, but use reassign it as a string in your function, I don't know if that is intentional or not, but type/fix as needed.另请注意,TS 在这里捕获了一个您已经犯过的错误,其中page值是您的 useState 中的一个数字,但是在您的 function 中将其重新分配为一个字符串,我不知道这是不是故意的,但是键入/修复如所须。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM