在使用Next.js(React)时要注重类型安全
首先
这篇文章的目的就是提醒大家,在Next.js(React)开发中要重视类型安全。
虽然我们使用 TypeScript,但是最好避免使用 any 等描述,但是在不了解的情况下,也可能不得不妥协。
尽量不要妥协,我们要学习与类型安全相关的知识。
目标读者
使用Next.js(React)和Typescript进行开发的人,不自觉地过多地使用any类型。
文章的结构
如果按照以下的构成,只要牢牢掌握这些要点,我认为类型安全就能得到相当程度的保障,并且会感觉不错。
-
- 使用React.FC将为函数组件提供类型定义。
解释React.ReactNode
对React.ReactNode在children等中的使用进行解释。
使用HTML[○○]Element
通过HTML[○○]Element,可以将其视为对应的HTML元素。
HTML元素属性的类型信息
使用React.ComponentPropsWithoutRef获取HTML元素的属性。
熟练使用实用类型(utility type)
使用实用类型生成新的类型从现有类型。
1. 函数组件的类型定义
在React中,可以使用React.FC来定义函数组件的类型。
然而,由于React能够很好地进行类型推断,因此你可以根据个人喜好决定是否写入这个类型,不写也没问题。
如果要提出一個對於一応型定義記載的好處,那就是
-
- 関数コンポーネントであるということが一目でわかる
- ジェネリクスを使うことで型情報がまとまってスッキリする
大致就是这样了。
type Props = {
name: string;
age: number;
}
const Sample: React.FC<Props> = ({ name, age }) => {
return <div>{`name: ${name}, age: ${age}`}</div>;
}
2. 关于 React.ReactNode
React.ReactNode 表示了 React 组件可以作为子元素 (children) 接受的类型。
具体例包括组件、字符串、undefined等等…
如果不特别指定children的类型,应该使用React.ReactNode而不是any。
type Props = {
children: React.ReactNode;
};
const Sample: React.FC<Props> = ({ children }) => {
return <div>{children}</div>;
};
const Main: React.FC<Props> = () => {
return (
<div>
<Sample>
<div>sample</div>
</Sample>
<Sample>sample</Sample>
</div>
);
};
3. 使用HTML[○○]元素
有许多不同的类型,例如HTMLDivElement和HTMLButtonElement,但可以将它们用作表示HTML元素的类型。
例如,当使用forwardRef来处理类似SampleBtn下面的情况时,可以定义HTMLButtonElement 作为要处理的元素的类型。
import { forwardRef } from 'react';
type Props = {
children: string;
};
const SampleBtn: React.FC<Props> = forwardRef<HTMLButtonElement, Props>(({ children }, ref) => {
return <button ref={ref}>{children}</button>;
});
4. HTML元素的属性类型信息
可以使用React.ComponentPropsWithoutRef或React.ComponentPropsWithRef来获取HTML元素属性的类型信息。
区别在于是否包含Ref字样的名称。
type Props = { children: React.ReactNode } & React.ComponentPropsWithoutRef<'button'>;
const SampleBtn: React.FC<Props> = ({ children, ...buttonAttributes }) => {
return <button {...buttonAttributes}>{children}</button>;
};
export const Main = () => {
return (
<div>
{/* button要素の属性をすべて受け付けるので、typeが設定可能 */}
<SampleBtn type='button'>button</SampleBtn>
</div>
);
};
5. 熟练运用“实用型”技能
通过使用utility types,可以重用类型,使源代码更易于维护。
我认为常常使用的是Pick和Omit等等,可以通读一遍,以便随时准备使用。
如果你对以下内容感兴趣,请点击链接查看原文。
所需的
以下是公式。
Required是一个从T的所有属性中删除?的可选项的实用工具类型。
只读的
以下是公式。
Readonly是一个实用类型,用于将对象类型T的所有属性都设置为只读。
泛型T的部分
这里有一个公式。
Partial是一种实用类型,它将类型T的所有属性转换为可选属性。
选择<T, Keys>
以下是公式。
Pick是一个实用类型,它返回一个只包含指定键Keys的对象类型,该对象类型是从类型T派生的。
省略<T, Keys>
这是公式。
Omit是一个实用类型,它返回从对象类型T中排除了Keys指定属性的object类型。
排除 <T, U>
这里是公式。
Exclude是一个实用类型,它从联合类型T中删除指定类型U,并返回一个新的联合类型。
提取<T, U>
公式在这里。
Extract是一个实用型,它从联合类型T中提取出仅由类型U指定的类型返回。
让我们自定义上述的实用工具类型。
在中文中,我们很少直接使用 Required\ 或 Readonly\,通常只会用于需要将某个属性设为必需的情况。
在这种情况下,需要根据情况自定义实用程序类型。
我想要巧妙地利用现有的模板,以追求一个具有一致性的源代码!
type AddRequired<T, U extends keyof T> = Omit<T, U> & Required<Pick<T, U>>;
type BaseProps = {
children: React.ReactNode;
disabled: boolean;
};
type Props = AddRequired<BaseProps, 'children'>;
结束
使用TypeScript的话,尽量避免使用any,并且努力实现类型安全的实现。
我認為這關乎品質問題,所以我會堅持不妥協,繼續努力學習!