[英]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 个错误?
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() },
])
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.format
的row
类型是什么?
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
Value1
和Value2
定义的格式不明确
The compiler cannot tell row
's type between Row[Key] //Value1
and Row //Value2
编译器不能告诉row
的类型之间的Row[Key] //Value1
和Row //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.