为了使系统方便维护,我们会在项目中引入 Typescript,通过使用 TypeScript,可以更好地定义对象和函数的类型,减少错误,提高代码的可读性和可维护性。然而大部分新手刚接触 Typescript 或者 React ,不知道如何声明 Props,类组件,函数组件等。接下来让我们通过例子来走进 React 和 Typescript 世界。
常见的 Props 类型声明
下面是常见的类型声明,我们通过 type
关键字来声明,当然你也可以通过 interface
来声明。
type Props = { name: string; // 姓名 age: number; // 年龄 disabled: boolean; // 是否禁用 students: Array<{ name: string; // 姓名 age: number; // 年龄 }>; // 学生列表 people: { name: string; // 姓名 age: number; // 年龄 }; obj2: object; // 对象类型 obj3: {}; // 对象类型 onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; // 原生事件类型 onClick: (e: React.MouseEvent<HTMLButtonElement>) => void; // 原生事件类型 onSubmit: (val: string) => void; // 自定义事件 optional?: string; // 可选属性};
在 TypeScript 中,object 作为非原始类型,可能会引起误解。实际上,object 并不是指“任何对象”,而是指“任何非原始类型”。也就是说,object 表示的并不是数字、字符串、布尔值、符号、null 或 undefined。通常在声明对象时,我们不会用 object,而是会使用具体的对象的属性,例如上述的 people
,如果需要声明无法确定的键值对,可以用 Record<string, any>
类组件声明
上述声明一些常用的组件属性,我们来声明一个类组件,通常需要声明组件的 Props
和 State
interface Props { message: string;}interface State { count: number;}class App extends React.Component<Props, State> { state: State = { count: 0, }; render() { const { message } = this.props; const { count } = this.state; return ( <div> {message} {count} </div> ); }}
函数组件声明
我们都知道函数组件只需要一个 Props
,所以声明函数组件跟类组件还是有区别的,最简单的函数组件声明如下:
type Props = { name: string;};// 或者// interface Props {// name: string;// }const App = (props: Props) => { return props.name;};// 或者const App: React.FC<Props> = (props) => { return props.name;};// 或者const App: React.FC<Props> = ({ name }): string => { return name;};
组件 children 的类型
在 React 可以通过 children 来自定义子元素的渲染,我们可以这样声明:
interface Props { children: React.ReactNode;}// 或者interface Props { children: JSX.Element;}// 或者interface Props { children: React.ReactElement;}// 或者interface Props { children: React.ReactNode | JSX.Element;}
ReactNode 和 JSX.Element,ReactElement 的区别
// ============== ReactNode声明type ReactText = string | number;type ReactChild = ReactElement | ReactText;interface ReactNodeArray extends Array<ReactNode> {}type ReactFragment = {} | ReactNodeArray;type ReactNode = | ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;// ============== ReactElement声明type Key = string | number;interface ReactElement< P = any, T extends string | JSXElementConstructor<any> = | string | JSXElementConstructor<any>> { type: T; props: P; key: Key | null;}// ============== JSX.Element声明declare global { namespace JSX { interface Element extends React.ReactElement<any, any> {} }}
从上述定义看 JSX.Element
继承了 ReactElement
,只不过在 Props 和 State 声明指定为 any,更加通用,然而 ReactNode 更全面,通常我们 React 组件不一定是 JSX 元素,也可能是字符串,数字,空等类型,所以 children 用 ReactNode 来定义就对了。
hooks 声明
useState
声明 state 时,会接受一个泛型,如果我们不指定类型,通常会根据初始值自动推断出类型,下面的例子中,isDone
会被推断为 boolean 类型。
const [isDone, setDone] = useState(false);
如果 useState
参数是空,ts 会推断这个 state 是 undefined
,接下来我们看下面的例子,我们手动设置 name 为 "",ts 会抛出警告**。「****Argument of type '""' is not assignable to parameter of type 'SetStateAction
function App() { const [name, setName] = useState(); return ( <div> <button onClick={() => { setName(''); }} > click </button> {name} </div> );}
所以我们需要手动声明 state 的类型,即:
const [name, setName] = useState<string>(); // name = [string | undefined]
下面是一些常见的 useState 声明:
const [user, setUser] = useState<User | null>(null);setUser(newUser); // newUser 可以是 User 或者 nullconst [user, setUser] = useState<User>({}); // 报错 Argument of type '{}' is not assignable to parameter of type 'User | (() => User)'.const [user, setUser] = useState<User>({} as User); // 正常