繁体   English   中英

打字稿:推断对象内函数参数的查找类型

[英]Typescript: Infer lookup type for function parameter within object

定义通用表。 这需要数据和显示方式的架构。 这是如何调用它的简化示例。

<PagedTable
  data={[ username: 'rob', logins: 32 ]}
  schema={[
    {
      key: 'username',
      customRender: (targetData, row) => fnThatOnlyTakesStrings(targetData)
    } 
   ]}
 />

组件类具有以下签名:

class PagedTable<T> extends Component<Props<T>, any>

和道具:

interface Props<T> {
  data: T[] | null | undefined
  schema: ColumnDef<T>[]
}

interface ColumnDef<T> {
  key?: keyof T
  customRender?: <U extends keyof T>(
    targetData: T[U],
    rowData: T
  ) => string | JSX.Element
}

因此,可以正确推断该类的具体类型。

为了完整起见,在PagedTable内部,该单元格的简化呈现功能是:

renderCell(datum: T, { customRender, key }: ColumnDef<T>) {
  if (customRender) {
    return customRender(datum[key!], datum, key!)
  } else {
    return datum[key]
  }
}

我想要的是使targetData在使用customRender函数时获取正确的类型。 在此设置中,其类型为number | string number | string不正确。

有没有一种方法可以仅使用架构对象的key属性(保证是数据对象的键)来推断类型?

我认为您的主要问题是您可能不希望customRender成为通用函数。 并非任何customRender都可以为任何 UT[U] ,而是每个customRender希望有一个特定的 U 毕竟, (targetData, row) => fnThatOnlyTakesStrings(targetData)似乎期望targetData是一个string ,它仅适用于某些特定键,对吗?

因此,我认为您确实希望ColumnDefTU中都是通用的,如下所示:

interface ColumnDef<T, U extends keyof T> {
  key?: U
  customRender?: (
    targetData: T[U],
    rowData: T
  ) => string | JSX.Element
}

这最终需要传播到引用ColumnDef其他事物:

interface Props<T, U extends keyof T> {
  data: T[] | null | undefined
  schema: ColumnDef<T, U>[]
}

class PagedTable<T, U extends keyof T> extends React.Component<Props<T, U>, any> {
  renderCell(datum: T, cd: ColumnDef<T, U>) {
    if (cd.customRender) {
      return cd.customRender(datum[cd.key! as any as U], datum)
    } else {
      return datum[cd.key!]
    }
  }
}

(我不确定对renderCell所做的更改是否必要或是否符合预期,但是我遇到了与参数数量有关的错误,并且我对其进行了修复,直到没有抱怨为止。您应该对其进行更改以适合您的需求)。


希望能有所帮助。 祝好运。

如果我正确理解这一点,那么棘手的方面是schema可以具有多个不同类型的ColumnDef ,并且每个 ColumnDef的键需要与其customRender函数相对应。 为了让TypeScript为每个ColumnDef推断并强制使用不同的类型,您需要将每个ColumnDef包装在函数调用中。 然后我发现我需要显式指定T以便推理起作用。 希望不会有麻烦,因为我认为在实际应用程序中您已经具有T类型定义。 这段代码为我工作:

import * as React from "react";

interface Props<T> {
  data: T[] | null | undefined
  schema: WrappedColumnDef<T>[]
}

interface ColumnDef<T, U extends keyof T> {
  key?: U
  customRender?: (
    targetData: T[U],
    rowData: T
  ) => string | JSX.Element
}

const WRAPPED_COLUMN_DEF_MARKER = Symbol();
interface WrappedColumnDef<T> extends ColumnDef<T, any> { 
  [WRAPPED_COLUMN_DEF_MARKER]: undefined;
}

declare function wrapColumnDef<T, U extends keyof T>(arg: ColumnDef<T, U>): WrappedColumnDef<T> { 
  return arg as WrappedColumnDef<T>;
}

declare class PagedTable<T> extends React.Component<Props<T>, any> {}

declare function fnThatOnlyTakesStrings(arg: string): string;

interface MyDatum { 
  username: string;
  logins: number;
}

let x = <PagedTable<MyDatum>
  data={[{ username: 'rob', logins: 32 }]}
  schema={[
    wrapColumnDef({
      key: 'username',
      customRender: (targetData, row) => fnThatOnlyTakesStrings(targetData)
    })
   ]}
 />;

暂无
暂无

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

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