组件 Props 类型
本文档旨在为熟悉 TypeScript 的 React 开发人员提供基本指南和参考。
基本 Props 类型示例
您在 React+TypeScript 应用中可能用到的 TypeScript 类型列表
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** array of a type! */
names: string[];
/** string literals to specify exact string values, with a union type to join them together */
status: "waiting" | "success";
/** an object with known properties (but could have more at runtime) */
obj: {
id: string;
title: string;
};
/** array of objects! (common) */
objArr: {
id: string;
title: string;
}[];
/** any non-primitive value - can't access any properties (NOT COMMON but useful as placeholder) */
obj2: object;
/** an interface with no required properties - (NOT COMMON, except for things like `React.Component<{}, State>`) */
obj3: {};
/** a dict object with any number of properties of the same type */
dict1: {
[key: string]: MyTypeHere;
};
dict2: Record<string, MyTypeHere>; // equivalent to dict1
/** function that doesn't take or return anything (VERY COMMON) */
onClick: () => void;
/** function with named prop (VERY COMMON) */
onChange: (id: number) => void;
/** function type syntax that takes an event (VERY COMMON) */
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
/** alternative function type syntax that takes an event (VERY COMMON) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** any function as long as you don't invoke it (not recommended) */
onSomething: Function;
/** an optional prop (VERY COMMON!) */
optional?: OptionalType;
/** when passing down the state setter function returned by `useState` to a child component. `number` is an example, swap out with whatever the type of your state */
setState: React.Dispatch<React.SetStateAction<number>>;
};
object
作为非原始类型
object
在 TypeScript 中是一个常见的误解来源。它并不意味着“任何对象”,而是“任何非原始类型”,这意味着它表示任何不是 number
、bigint
、string
、boolean
、symbol
、null
或 undefined
的类型。
在 React 中,您不太可能经常需要为“任何非原始值”添加类型,这意味着您可能不会经常使用 object
。
空接口、{}
和 Object
空接口、{}
和 Object
都表示“任何非 nullish 值”,而不是您可能认为的“空对象”。使用这些类型是常见的误解来源,不建议使用。
interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object`
let value: AnyNonNullishValue;
// these are all fine, but might not be expected
value = 1;
value = "foo";
value = () => alert("foo");
value = {};
value = { foo: "bar" };
// these are errors
value = undefined;
value = null;
有用的 React Props 类型示例
适用于接受其他 React 组件作为 Props 的组件。
export declare interface AppProps {
children?: React.ReactNode; // best, accepts everything React can render
childrenElement: React.JSX.Element; // A single React element
style?: React.CSSProperties; // to pass through style props
onChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target
// more info: https://react-typescript-cheatsheet.reactjs.ac.cn/docs/advanced/patterns_by_usecase/#wrappingmirroring
props: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its ref
props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref
}
React 18 之前的小型 React.ReactNode
边缘情况
在React 18 类型更新之前,此代码通过了类型检查,但在运行时出现了错误
type Props = {
children?: React.ReactNode;
};
function Comp({ children }: Props) {
return <div>{children}</div>;
}
function App() {
// Before React 18: Runtime error "Objects are not valid as a React child"
// After React 18: Typecheck error "Type '{}' is not assignable to type 'ReactNode'"
return <Comp>{{}}</Comp>;
}
这是因为 ReactNode
包括 ReactFragment
,在 React 18 之前允许类型为 {}
。
React.JSX.Element 与 React.ReactNode?
引用@ferdaber:更专业的解释是,有效的 React 节点与 React.createElement
返回的值并不相同。无论组件最终渲染什么,React.createElement
始终返回一个对象,即 React.JSX.Element
接口,但 React.ReactNode
是组件所有可能返回值的集合。
React.JSX.Element
->React.createElement
的返回值React.ReactNode
-> 组件的返回值
更多讨论:ReactNode 与 React.JSX.Element 不重叠的地方
类型还是接口?
您可以使用类型或接口为 Props 和 State 添加类型,因此自然会产生一个问题——您应该使用哪一个?
TL;DR
在需要使用类型之前,请使用接口 - orta。
更多建议
这是一个有用的经验法则
在编写库或第三方环境类型定义时,始终使用
interface
来定义公共 API,因为这允许使用者通过声明合并扩展它们,如果某些定义缺失。出于一致性考虑,以及因为
type
限制性更强,请考虑为您的 React 组件 Props 和 State 使用type
。
您可以在TypeScript 2.7 中的接口与类型别名中阅读更多关于此经验法则背后的原因。
TypeScript 手册现在还包括关于类型别名与接口之间的区别的指南。
注意:在大型项目中,出于性能原因,更倾向于使用接口(请参阅 Microsoft 关于此的官方说明),但谨慎对待
类型适用于联合类型(例如 type MyType = TypeA | TypeB
),而接口更适合声明字典形状,然后实现
或扩展
它们。
类型与接口的有用表格
这是一个细致入微的话题,不要太纠结。这是一个方便的表格
方面 | 类型 | 接口 |
---|---|---|
可以描述函数 | ✅ | ✅ |
可以描述构造函数 | ✅ | ✅ |
可以描述元组 | ✅ | ✅ |
接口可以扩展它 | ⚠️ | ✅ |
类可以扩展它 | 🚫 | ✅ |
类可以实现它 (implements ) | ⚠️ | ✅ |
可以与另一个同类进行交叉 | ✅ | ⚠️ |
可以与另一个同类创建联合 | ✅ | 🚫 |
可用于创建映射类型 | ✅ | 🚫 |
可以使用映射类型对其进行映射 | ✅ | ✅ |
在错误消息和日志中扩展 | ✅ | 🚫 |
可以增强 | 🚫 | ✅ |
可以递归 | ⚠️ | ✅ |
⚠️ 在某些情况下
(来源:Karol Majewski)