简体   繁体   English

如何让这个受歧视的工会发挥作用?

[英]How to make this discriminated union work?

Trying to create a simple table component, but having a bit of trouble with making the typings work as I want.试图创建一个简单的表格组件,但在按照我的需要进行打字时遇到了一些麻烦。

TL;DR How do write the type for Column3 in this Playground Link so that the 2 errors go away? TL;DR 如何在此Playground Link 中编写Column3的类型,以便Column3 2 个错误?

This works这有效

So given the following common stuff:所以给出以下常见的东西:

type Data = { id: number, name: string };
const data: Data[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
]

type Header = { header: string };

Here's a variant that makes you supply a typesafe field name, and optionally a formatting function for the value of that field:这是一个让您提供类型安全字段名称的变体,以及可选的该字段值的格式化函数:

type Value1<Row> = {
  [Key in keyof Row]: {
    field: Key;
    format?: (value: Row[Key]) => string;
  };
}[keyof Row];

type Column1<Row> = Header & Value1<Row>;

const makeTable = <R>(r: R[], c: Column1<R>[]) => {}
makeTable(data, [
  { header: 'Id', field: 'id' },
  { header: 'Name', field: 'name', format: value => value.toUpperCase() },
])

Here's a variant that makes you only supply a formatting function, and instead of a value you get the row object:这是一个变体,它使您只提供格式化函数,而不是获得行对象的值:

type Value2<Row> = {
  format: (row: Row) => string;
};

type Column2<Row> = Header & Value2<Row>;

const makeTable = <R>(r: R[], c: Column2<R>[]) => {};
makeTable(data, [
  { header: 'Id', format: row => row.id.toString() },
  { header: 'Name', format: row => row.name.toUpperCase() },
])

This does not...这不...

The issue comes when I would like to combine these two, because for some columns the first variant makes sense, and for others the second one does.当我想将这两者结合起来时,问题就出现了,因为对于某些列,第一个变体是有意义的,而对于其他列,第二个变体是有意义的。

I figured I could do it as follows, but Typescript is not happy...我想我可以这样做,但 Typescript 不高兴......

type Column3<Row> = Header & (Value1<Row> | Value2<Row>); // <-- Non-working discriminated union?

const makeTable = <R>(r: R[], c: Column3<R>[]) => { };
makeTable(data, [
  { header: 'Id', field: 'id' },
  { header: 'Name', field: 'name', format: value => value.toUpperCase() },
  { header: 'Test', format: row => `${row.id} : ${row.name}` },
])

Apparently the type of data is now incorrect, and also that row in the third column for some reason is implicitly any , which it of course shouldn't be.显然, data类型现在不正确,而且由于某种原因,第三列中的那一row隐含地是any ,这当然不应该是。

I can "fix" the type of data by being explicit about the generic Row type:我可以通过明确通用Row类型来“修复” data类型:

makeTable<Data>(data, [
  { header: 'Id', field: 'id' },
  { header: 'Name', field: 'name', format: value => value.toUpperCase() },
  { header: 'Test', format: row => `${row.id} : ${row.name}` },
])

But row is still implicitly any , and it really shouldn't be necessary to be explicit here, so something is definitely not right here... but... I don't understand what's going on here.但是row仍然是隐含的any ,在这里真的没有必要明确,所以这里肯定有什么不对......但是......我不明白这里发生了什么。 😕 😕

What am I misunderstanding here?我在这里误解了什么?

So, let's chalk the wrong type on data up to generic default types (because Row is never defined as an actual type, I think Typescript defaults it to string at some point for purposes of type resolution (I may be wrong on this)).因此,让我们将错误的data类型归为通用默认类型(因为Row从未定义为实际类型,我认为 Typescript 在某些时候将其默认为string以进行类型解析(我可能在这方面错了))。

The type of row being any in the format functions is due to Typescript not inferring types for function arguments . format函数中的any row类型是由于 Typescript 没有推断函数参数的类型 That thread has a lot of good discussion as to why.该线程对原因进行了很多很好的讨论。 In fact, at one point in the discussion, it is mentioned that事实上,在讨论中的某一点提到,

there's nothing in TypeScript today that infers information about a variable from its usage.今天的 TypeScript 没有任何东西可以从变量的使用中推断出有关变量的信息。

For a short example of the complexities that can develop, what is the type of row in z.format ?对于可以发展的复杂性的简短示例, z.formatrow类型是什么?

type t = { format: (row: string) => string } | { format: (row: number) => number }
const z: t = {format: row => row }

Without additional annotation during usage:使用过程中无需额外注释:

const z: t = {format: (row: number) => row}

The compiler cannot easily tell what shape row is supposed to be.编译器无法轻易判断row应该是什么形状。 Unfortunately, this forces us to be explicit at times that we wouldn't expect to have to be.不幸的是,这迫使我们有时要明确,而我们并不期望必须如此。

To 'fix' the type for Column3 , you just have to be a little more explicit in your usage:要“修复” Column3的类型,您只需在使用中更加明确:

type Column3<Row> = Header & (Value1<Row> | Value2<Row>);
type R = Data
(<R>(r: R[], c: Column3<R>[]) => {})(data, [
  { header: 'Id', field: 'id' },
  { header: 'Name', field: 'name', format: value => value.toUpperCase() },
  { header: 'Test', format: (row: R) => `${row.id} : ${row.name}` },
])

Format is ambigeous as defined in both Value1 and Value2 Value1Value2定义的格式不明确
The compiler cannot tell row 's type between Row[Key] //Value1 and Row //Value2编译器不能告诉row的类型之间的Row[Key] //Value1Row //Value2
I suggest you to switch the format of both Value1 and Value2 :我建议您切换 Value1 和 Value2 的format

type Value1<Row> = {
  [Key in keyof Row]: {
    field: Key;
    format_field?: (value: Row[Key]) => string;
  };
}[keyof Row];

type Value2<Row> = {
  format_row: (row: Row) => string;
};

(<R>(r: R[], c: Column3<R>[]) => {})<Data>(data, [
  { header: 'Id', field: 'id' },
  { header: 'Name', field: 'name', format_field: value => value.toUpperCase() },
  { header: 'Test', format_row: row => `${row.id} : ${row.name}` },
]) //No more ambigeous

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

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