gpt4 book ai didi

reactjs - 为什么在使用泛型的特定设置中将可选字段的类型设置为未知?

转载 作者:行者123 更新时间:2023-12-04 12:36:35 24 4
gpt4 key购买 nike

我试图在 carbon-components-react 上修复一些关于 DataTable 的类型,但被困在使一个属性可选。我到了创建自己的演示来展示这个问题的地步:

import React from "react";

interface Human {
name: string;
age: number;
address?: string;
}

interface ButtonProps<R extends Human> {
name: R["name"];
age: R["age"];
address?: R["address"];
onClick?: Function;
}

interface RenderProps<R extends Human> {
human: R;
getButtonProps(data?: ButtonProps<R>): ButtonProps<R>;
}

export interface DataTableProps<R extends Human> {
rows: R[];
render(props: RenderProps<R>): React.ReactNode;
}

class DataTable<R extends Human> extends React.Component<DataTableProps<R>> {
render() {
return this.props.rows.map((h) => {
const buttonProps: () => Human = () => ({ ...h }); // This is only to be able to see the type of buttonProps in IDE

return this.props.render({ human: h, getButtonProps: buttonProps });
});
}
}

interface TableButtonProps {
name: string;
address?: string;
}

const TableButton = (props: TableButtonProps) => (
<button>Button for {props.name}</button>
);

export const Demo = () => {
// This works
const man1: Human = {
name: "Anna",
age: 30,
};

// This seems to be ok since "address" is optional
// but this isn't working!
const man2 = {
name: "Tom",
age: 32,
};

// This works
const man3 = {
name: "John",
age: 40,
address: "404 Street Unknown",
};

return (
<DataTable
rows={[man2]}
render={({ getButtonProps }) => {
return <TableButton {...getButtonProps()} />;
}}
></DataTable>
);
};

我在 TableButton 上遇到的错误:
Type '{ name: string; age: number; address?: unknown; onClick?: Function | undefined; }' is not assignable to type 'TableButtonProps'.
Types of property 'address' are incompatible.
Type 'unknown' is not assignable to type 'string | undefined'.
Type 'unknown' is not assignable to type 'string'. TS2322
address属性(property)有 unknown出于某种原因键入。当我使用像 man1 这样的语法时或 man3一切正常,您可以在 rows= 更换它.为什么是 man2即使类型仍然设置为扩展 Human 也不起作用? TypeScript 会迷路吗?

您可以通过使用 npx create-react-app mydemo --template=typescript 初始化项目来测试这一点。

//编辑:
interface ButtonProps<R extends Human> {
name: Human["name"];
age: Human["age"];
address?: Human["address"];
onClick?: Function;
}

似乎也在解决这个问题并“保护” address类型。

最佳答案

您可能已经注意到问题出在 address属性(property)。
出于某种原因,它被推断为 unknown .
通常,您会收到 unknown因为 TS 无法推断通用参数。
见示例:

const foo = <T,>(arg: T): T => arg

// const foo: <unknown>(arg: unknown) => unknown
const result = foo() // unknown
考虑这个例子:
type GetAddress<T extends Human> = T extends Human ? T['address'] : never

type Result = GetAddress<{ name: 'John', age: 42 }> // unknown
从技术上讲, { name: 'John', age: 42 }可分配给 Human因为 address是可选的。另一方面, T['address']不能退货 undefined因为它不是 javascript - 它是 typescript 的(斯巴达)类型系统。因此,如果 TS 无法获取/推断 address属性,最安全的选择是返回 unknown ;
让我们回到你的例子。
为了可读性,我用自定义 Component 替换了内置的 react 组件类(class):
import React from "react";

interface Human {
name: string;
age: number;
address?: string;
}

interface ButtonProps<R extends Human> {
name: R["name"];
age: R["age"];
address?: R["address"];
onClick?: Function;
}

interface RenderProps<R extends Human> {
human: R;
getButtonProps(data?: ButtonProps<R>): ButtonProps<R>;
}


export interface DataTableProps<R extends Human, Address extends Human['address']> {
rows: R[];
render: (props: RenderProps<R>) => React.ReactNode;
}

class Component<Props>{
constructor(public props: Props) {
this.props = props
}
}

class Foo<T extends Human> extends Component<DataTableProps<T, T['address']>>{
render = () => {
return this.props.rows.map(
(row) =>
this
.props
.render(
{ human: row, getButtonProps: () => ({ ...row }) }
)
);
}
}

const human = { age: 42, name: 'John Doe' }

const jsx = new Foo({
rows: [human],
render: ({ getButtonProps }) => {
const x = getButtonProps().address
return <TableButton {...getButtonProps()} />;
}
})
请在此行抢夺 Component<DataTableProps<T, T['address']>>我特意加了 T['address']向您展示 TS 将如何推断它。
enter image description here
如您所见 - 这是未知的。所以,我们最终得到了对象 {age: number, name: string, address: unknown}不能分配给 Human因为 address .
你可能会问:
等等,但为什么它适用于显式类型? const human: Human = { age: 42, name: 'John Doe' }这是因为没有显式类型, R推断为 {age: number, name: string}而显式 Human类型它被推断为 {age:number, name: string, address?:string, onClick?:Function} .
因此,如果你想修复你的错误,你只需要为 DataTable 提供明确的通用参数。类(class)。
见示例:
    <DataTable<Human> // <---------------- fix is here
rows={[man2]}
render={({ getButtonProps }) => {
return <TableButton {...getButtonProps()} />;
}}
></DataTable>
顺便说一句,非常有趣的问题。
更新
你也可以使用这个助手:
/**
* Replace all unknown props with appropriate Human props
*/
type HandleProps<T extends Human> = T & Omit<Human, keyof T>

// ............

class DataTable<R extends Human> extends React.Component<DataTableProps<HandleProps<R>>> {
render() {
return this.props.rows.map((h) => {
const buttonProps: () => Human = () => ({ ...h });

return this.props.render({ human: h, getButtonProps: buttonProps });
});
}
}
Playground
它将替换所有 unknown合适的 Prop Human Prop

关于reactjs - 为什么在使用泛型的特定设置中将可选字段的类型设置为未知?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61069106/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com