logo科技微讯

Typescript type 一个函数的方法

作者:科技微讯
日期:2022-11-17
📝 笔记

这篇文章汇总了 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

第四、第五种写法就是所谓的 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 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 CheatsheetsDan 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>。除此之外还有 ReadonlyArrayPromiseSet 等。

声明一个 object type 可以用 typeinterface 关键词,Kent C. Dodds 喜欢用 type。

Given the choice between type, interface, and declare function, I think I prefer type personally, unless I need the extensibility that interface offers. I'd only really use declare if I really did want to tell the compiler about something that it doesn't already know about (like a library). 出处

Signatures

以上函数都是普通的函数,有些函数除了可以被调用,还附带了一些属性,而有些函数是 factory function,可以通过 new 关键词创建一个对象,这两种函数的 type 可以分别使用所谓的 Call SignaturesConstruct Signatures 去定义。

还有另外两种 signature:overload signatures 和 implementation signature,它们是用来实现 Function Overloads 的。

<> 符号

<> 符号在 typescript 中有两种意思:

  • 用于 generic function 或 generic interface:
    • 用来声明一个 type 变量:function fn<T>(param:T):T{return param}
    • 用来传入一个 type 值:fn<number>(1)Pick<Array<number>, 'length'>
  • 用于 type assertionsconst <number>num = 2

通过 <> 进行 type assertion 和 jsx 冲突,因为 jsx 会把 <number> 理解为一个 jsx tag,为了解决这个问题,typescript 在引入 tsx 的同时,也引入了 asconst num = 2 as number


相关阅读:

donation赞赏
thumbsup0
thumbsdown0
暂无评论