这篇文章汇总了 Typescript 定义一个函数类型的多种方法,除了常规的 function type expression 外,还有 generic function、generic type 等等。React 函数式组件其实也是一个函数,所以这篇文章还整理了如何 type 一个 React 组件的方法,更准确地说,是 type 一个 React 组件的 props 的方法。
const fn: (param: number) => string = (param) => param.toString();
这种写法叫 function type expression
。
也可以这样写:
type FnType = (param: number) => string;
const fn: FnType = (param) => param.toString();
也可以把 type 改为 object type 的形式,后文提到的 Call Signatures、Construct Signatures 用的就是这种形式:
type FnType = {
(param: number): string;
};
const fn: FnType = (param) => param.toString();
const fn = (param: number): string => param.toString();
function fn(param: number): string {
return param.toString();
}
function fn<T>(param: T): string | void {
if (typeof param === "number") {
return param.toString();
}
}
如果函数的参数和函数返回的结果都是一样的 type,可以写成:
function fn<T>(param: T): T | void {
if (typeof param === "number") {
return param.toString();
}
}
interface genericInterface<T> {
(param: T): T | void;
}
const fn: genericInterface<string> = (param) => param;
第四、第五种写法就是所谓的 generic function,需要把 <T>
理解为一个变量(或者参数),调用这种 generic function 时需要给 <T>
传入一个值,传值方法是在函数名后添加 <my_type>
,如下:
//这个例子把 `number` 这个 type 传入了 `<T>`,相当于传入了 2 个参数
//一个参数是 number 这个 type,另一个参数是数字 1
const result = fn<number>(1);
console.log(result);
写法四相当于变成了:
function fn(param: number): string | void {
if (typeof param === "number") {
return param.toString();
}
}
写法五相当于变成了:
function fn(param: number): number | void {
if (typeof param === "number") {
return param.toString();
}
}
如果不向 <T>
传入一个 type,typescript 会通过 inference 产生一个默认的 type,例如 fn(2)
,那 <T>
会被 infer 为 number
。
写法六是一个 generic interface,和 generic function 一样,需要给 <T>
传入一个值,但 generic function 是在调用函数的时候传入 <T>
,而 generic interface 是在声明一个函数时传入 <T>
。generic interface 只适用于 function expression。
type 一个 react 函数式组件就用到了 generic interface:
interface buttonProps {
text: string;
}
//JSX.Element 可写可不写
const Button: React.FC<buttonProps> = ({ text }): JSX.Element => {
return <button>{text}</button>;
};
相当于:
interface buttonProps {
text: string;
}
const Button = ({ text }: buttonProps) => {
return <button>{text}</button>;
};
React + TypeScript Cheatsheets、Dan Abramov 都不建议使用 React.FC<>
,Dan Abramov 建议这样 type children:
type Props = {
children?: React.ReactNode
};
function Component({children}: Props): React.ReactNode {
...
}
注意他的例子返回一个 React.ReactNode
,渲染这个组件时可能会提示 'ReactNode' is not a valid JSX element
的错误,这时可删除 React.ReactNode
或改为 JSX.Element
。事实上,最好的做法是不写 return type。
不仅 interface 可以 generic,type alias 也可以 generic:
type Box<Type> = {
contents: Type;
};
Array
是 typescript 内置的一个 generic type(generic interface),number[]
其实就是 Array<number>
。除此之外还有 ReadonlyArray
、Promise
、Set
等。
声明一个 object type 可以用 type
或 interface
关键词,Kent C. Dodds 喜欢用 type。
Given the choice between
type
,interface
, anddeclare function
, I think I prefertype
personally, unless I need the extensibility thatinterface
offers. I'd only really usedeclare
if I really did want to tell the compiler about something that it doesn't already know about (like a library). 出处
以上函数都是普通的函数,有些函数除了可以被调用,还附带了一些属性,而有些函数是 factory function,可以通过 new
关键词创建一个对象,这两种函数的 type 可以分别使用所谓的 Call Signatures、Construct Signatures 去定义。
还有另外两种 signature:overload signatures 和 implementation signature,它们是用来实现 Function Overloads 的。
<>
符号在 typescript 中有两种意思:
function fn<T>(param:T):T{return param}
;fn<number>(1)
、Pick<Array<number>, 'length'>
const <number>num = 2
通过 <>
进行 type assertion 和 jsx 冲突,因为 jsx 会把 <number>
理解为一个 jsx tag,为了解决这个问题,typescript 在引入 tsx 的同时,也引入了 as
:const num = 2 as number
。
相关阅读: