我想在数组中键入对象,以便对象的一个属性(这里是foo)隐式地 for each 对象分别定义另一个属性(bar)的类型.

这可能吗?

type Foo<T> = {
  foo: T;
  bar: T;
};

const foos: Foo<any>[] = [ // <-- What to put here instead of "any"?
  { foo: "text", bar: "this is ok"}, 
  { foo: 666, bar: "this is NOT ok" }, // <-- should show error (must be of type number)
];

在示例foobar中有相同的类型,在我的实际场景中,我有:

type Column<TData, TValue> = {
  accessorFunction: (data:TData) => TValue,
  cell: (info:TValue) => any
}

const columns = [
  {
    accessorFunction: ()=> "test",
    cell: (info) => info   // <-- info should be of type string
]

但我想这也有同样的打字问题.

推荐答案

(我将关注你的Column个例子,忽略Foo,因为推理问题是不同的,你关心的是Column而不是Foo).

从概念上讲,像"一个对象集合,对于我知道的特定D,每个对象的类型都是Column<D, V>,但对于一些我不关心的V",需要existentially quantified generic types.(我想你需要知道D,因为如果你不知道,就没有办法打电话给accessorFunction).

但是很少有语言支持这种类型,TypeScript也不支持;有关相关功能请求,请参阅microsoft/TypeScript#14466.如果我们真的有,你可以说Array<Column<D, exists V>>.但我们没有,所以我们不能.


有一些方法可以对存在类型进行编码.general方法使用类似Promise的数据 struct 来反转具有generic个回调的控制流.泛型函数允许调用者指定类型参数,而实现者只知道它是some类型.看起来是这样的:

type SomeColumn<D> = <R>(cb: <V>(col: Column<D, V>) => R) => R;

SomeColumn<D>就像Promise<Column<D, ??>>then()法.它接受回调,然后大概调用它所持有的底层Column<D, V>的回调.要将Column<D, V>转换为SomeColumn<D>,我们可以使用辅助函数:

const someColumn = <D, V>(col: Column<D, V>): SomeColumn<D> => cb => cb(col);

然后,您的数组可以如下所示:

const columns = [
    someColumn({
        accessorFunction: (a: string) => "test",
        cell: (info) => info.toUpperCase()
    }),
    someColumn({
        accessorFunction: (a: string) => a.length,
        cell: info => info.toFixed(2)
    })
];

columnsSomeColumn<string>型的.如果我们想处理数组,我们必须添加一个嵌套回调.比如说:

const results = columns.map(
    sc => sc(
        col => col.cell(col.accessorFunction("hello"))
    )
)
// const results: any[]
console.log(results); // ["TEST", "5.00"]

注意,我正在做的是我能用Column<string, V>做的唯一有用的事情,我不知道V...也就是说,拨打col.cell(col.accessorFunction(someString)).无论如何,results是一个由cell()个输出组成的数组(您输入的是any,所以我们有any[]个).


这对于您的用例来说是过度的.也许你们所关心的只是cell()方法输入的推断,你们不介意columns是一个类似Array<Column<string,string> | Column<string, number>>的array.如果是这样,您可以保留helper函数,但只需go 掉其中任何类似Promise的行为.它接受Column<D, V>并返回Column<D, V>:

const column = <D, V>(col: Column<D, V>) => col;

const columns = [
    column({
        accessorFunction: (a: string) => "test",
        cell: (info) => info.toUpperCase()
    }),
    column({
        accessorFunction: (a: string) => a.length,
        cell: info => info.toFixed(2)
    })
]; // okay

但您会发现很难通过编程方式处理这些问题:

const results = columns.map(
    col => col.cell(col.accessorFunction("hello")) // error!
    // -----------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // compiler doesn't know that this will always be the right type for col.cell
);

这里也有一些变通方法,但它们很烦人,如果小心正确地执行,您还可以使用类型断言:

const results = (columns as Array<Column<string, any>>).map(
  col => col.cell(col.accessorFunction("hello"))
);

Playground link to code

Typescript相关问答推荐

React Router SR:useLocate()只能在路由组件的上下文中使用

如何在类型脚本中使用条件陈述循环

具有条件通用props 的react 类型脚本组件错误地推断类型

Typescript自定义键映射

创建一个通用上下文,允许正确推断其中的子项

动态判断TypScript对象是否具有不带自定义类型保护的键

无法从应用程序内的库导入组件NX Expo React Native

JS Redux Devtools扩展不工作

角形标题大小写所有单词除外

在TypeScript中,除了映射类型之外还使用的`in`二进制运算符?

带占位符的模板文字类型何时扩展另一个?

material 表不渲染任何数据

类型{}上不存在属性&STORE&A;-MobX&A;REACT&A;TYPE脚本

Next.js错误:不变:页面未生成

如何在typescript中设置对象分配时的键类型和值类型

垫表页脚角v15

如何合并两个泛型对象并获得完整的类型安全结果?

为什么TypeScrip不能解析对象析构中的赋值?

TS 排除带点符号的键

为什么 TypeScript 类型条件会影响其分支的结果?