【问题标题】:Typescript: Infer lookup type for function parameter within object打字稿:推断对象内函数参数的查找类型
【发布时间】:2018-08-15 23:01:30
【问题描述】:

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

<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,这是不正确的。

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

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    我认为您的主要问题是您可能不希望 customRender 成为通用函数。不应该是任何customRender 可以将T[U] 用于任何 U,而是每个customRender 都需要一个特定 U。毕竟,(targetData, row) =&gt; 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 所做的更改是否是必要的,或者它的行为是否符合预期,但我遇到了与参数数量有关的错误,我修复了它直到它没有抱怨。你应该改变它以满足您的需要)。


    希望对您有所帮助。祝你好运。

    【讨论】:

    • 很快就会试用,但现在,+1 as any as U
    【解决方案2】:

    如果我理解正确的话,棘手的方面是schema 可以有多个不同类型的ColumnDefs,并且 每个 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)
        })
       ]}
     />;
    

    【讨论】:

    • 很快就会尝试,但现在,+1 用于演示如何在 JSX 组件上指定类型。
    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2020-03-01
    • 2018-09-19
    • 2021-07-22
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多