React + ts学习笔记

  • Post author:
  • Post category:其他




前提准备:



环境配置



安装node.js

官网安装:当前使用版本18.15.0



安装新的react应用:

运行命令新建react-app

npx create-react-app study-ts-app


当前版本:
  • “react”: “^18.2.0”,
  • “react-dom”: “^18.2.0”,

    如果出现如下问题,无法安装依赖

    image.png

    可以尝试运行npm i react,如果报错,执行推荐命令

    image.png

    运行命令:
sudo chown -R 502:20 "/Users/rcdl/.npm"

命令详解参考:

https://blog.csdn.net/wangcheeng/article/details/128165359


然后继续执行

create-react-app



安装typeScript

npm i typescript --save
  • “typescript”: “^5.0.4”,



在react中使用ts

参考:

https://juejin.cn/post/7021674818621669389



在类组件

参数没有都可以被省略

// 使用方式:React.Component<P, S={}>{...}

// 示例:
import * as React from 'react'
interface IProps {
  name: string;
}

interface IState {
  age: number;
}

class Hello extends React.Component<IProps, IState> {
  state = {
    age: 18
  };

  render() {
    return (
      <div>
        {this.state.age}
        {this.props.name}
      </div>
    );
  }
}

export default Hello;


React.Component

中P是

props

类型的定义,S是

state

类型的定义,都是泛型接口

React.PureComponent<P, S={} SS={}>{...}


React.PureComponent

前两个值含义相同,第三个值代表

getSnapshotBeforeUpdate

的返回值



在函数组件

在函数组件中,也可以接收一个参数(可省),参数表示

props

的类型,示例如下

interface IProps {
  name: string
}

const HelloFunction = (props: IProps) => {
  const {name} = props;

  return (
    <div className="App">
      <h1>hello world</h1>
      <h2>{name}</h2>
    </div>
  );
}

export default HelloFunction;

定义函数组件的第二种方式,

React.FunctionComponent<P={}>

来定义,也可以使用其简写

React.FC<P={}>

,参数可省,示例如下

interface IProps {
  name: string
}

const App: React.FC<IProps> = (props) => {
  const {name} = props;
  return (
    <div className="App">
      <h1>hello world</h1>
      <h2>{name}</h2>
    </div>
  );
}

export default App;

当使用这种形式来定义函数组件时,props中默认会带有children属性,它表示该组件在调用时,其内部的元素,来看一个例子,首先定义一个parent组件,组件中引入了Child1和Child2组件:

import Child1 from "../childFunction/child1";
import Child2 from "../childFunction/child2";

interface IProps {
  name: string;
}
const Parent: React.FC<IProps> = (props) => {
  const { name } = props;
  return (
    <Child1 name={name}>
      <Child2 name={name} />
      my test content
    </Child1>
  );
};

export default Parent;

child1组件如下

interface IProps {
  name: string;
}
const Child1: React.FC<React.PropsWithChildren<IProps>> = (props) => {
  const { name, children } = props;
  console.log(children);
  return (
    <div className="App">
      <h1>hello child1</h1>
      <h2>{name}</h2>
    </div>
  );
};

export default Child1;

这里的props.children是默认属性,输出结果是children的内容,就是parent组件中child1包裹起来的child2对象和文字

image.png



useState

如定义一个数字类型,有初始值可以直接写

const [flag, setFlag] = useState<number>(1)

没有初始值或初始值为null可以写成:

const [flag, setFlag] = useState<number | null>()

如初始化是一个对象

在确定对象中的属性类型和数量时,可以使用

泛型

定义

interface list {
    title: string,
    type: boolean
  }
  const [list, setList] = useState<list>({ title: '', type: false })
  setList({ title: '', type: true })

在未知对象属性数量和类型时

???

其他类型见下方

扩展:ts基本类型



useRef

react写法

const frameRef = useRef(null);

ts写法

// 改变值时类型null不加也没显示报错--存疑?
const frameRef = useRef<HTMLInputElement | null>(null)

实例:

  const myText = useRef<HTMLInputElement | null>(null);
  console.log(myText.current) // null
  const changeText = (e) => {
  // myText的值会随着input中内容的输入实时改变
    myText.current.innerHTML = e.target.value;
  }

  return (
    <div>
      <input type="text" onChange={(e) => changeText(e)} />
      <h2 ref={myText} />
    </div>
  );



useMemo

useMemo返回的是一个值,所以需要有返回类型,如果返回类型和定义类型不同会报错

注:useCallback不需要定义返回值类型,因为他返回的是一个函数

const calculatedValue1 = useMemo<number>(() => a ** 2, [a]);



事件处理



常见的Event事件对象如下:


  • 剪切板事件对象

    :ClipboardEvent<T = Element>

  • 拖拽事件对象

    :DragEvent<T = Element>

  • 焦点事件对象

    :FocusEvent<T = Element>

  • 表单事件对象

    :FormEvent<T = Element>

  • Change事件对象

    :ChangeEvent<T = Element>

  • 键盘事件对象

    :KeyboardEvent<T = Element>

  • 鼠标事件对象

    :MouseEvent<T = Element, E = NativeMouseEvent>

  • 触摸事件对象

    :TouchEvent<T = Element>

  • 滚轮事件对象

    :WheelEvent<T = Element>

  • 动画事件对象

    :AnimationEvent<T = Element>

  • 过渡事件对象

    :TransitionEvent<T = Element>

例如:


MouseEvent

是上面的鼠标事件对象(click事件)

HTMLDivElement

代表该函数作用于一个div元素

const handleChangeCurrent = (e: React.MouseEvent<HTMLDivElement>) => {}



事件处理函数的类型声明

type EventHandler<E extends SyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"];

type ReactEventHandler<T = Element> = EventHandler<SyntheticEvent<T>>;
// 剪切板事件处理函数
type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
// 复合事件处理函数
type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
// 拖拽事件处理函数
type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
// 焦点事件处理函数
type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
// 表单事件处理函数
type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
// Change事件处理函数
type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
// 键盘事件处理函数
type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
// 鼠标事件处理函数
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
// 触屏事件处理函数
type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
// 指针事件处理函数
type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
// 界面事件处理函数
type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
// 滚轮事件处理函数
type WheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>;
// 动画事件处理函数
type AnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>;
// 过渡事件处理函数
type TransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>;**



扩展:ts基本类型



布尔值true/false:boolean

let isDone: boolean = false;



数字:number

ts支持二进制、八进制、十进制和十六进制字面量。

let age: number = 6; 



字符串:string

let name: string = 'jack';



null和undefined

let u: undefined = undefined;
let n: null = null;



Symbol

Symbol是不可改变且唯一的。

let sym: symbol = Symbol()



数组:[]

定义数组的两种方式:

1 在元素类型后接上 []

2 使用数组泛型,Array<元素类型>

let list: number[] = [1, 2, 3];          //定义各项都是数字的数组。
let list: Array<number> = [1, 2, 3];     
let list: string[] = ['1', '2', '3'];          //定义各项都是字符串的数组。
let list: Array<string> = ['1', '2', '3'];    



元组 Tuple

元组类型可以定义已知元素数量和各元素类型的数组。

当访问已知索引的元素,会得到正确的类型;当访问越界的元素,会使用

联合类型

代替。

let data: [string, number];
data = ['jack', 20];



枚举:enum

枚举类型可以为一组数值赋予名字。默认情况下,元素从0开始编号,也可以手动指定元素的值。

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

枚举类型还可以根据枚举的值得到他的名字。

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName);  // Green



Any

any用于在不确定变量类型时使用。比如用户输入或第三方代码库引用,或者定义不同类型元素的数组。

ts默认这些值是不需要类型检查直接编译。

let notSure: any = 4;
notSure = "a string";
let list: any[] = [1, true, "jack"];



Unknown

与any类似,但是会做类型检查再编译。

function divide(param: unknown) {
  // return param / 2;  // 不知道params的类型使用了运算符会编译错误
  return param as number / 2;
}



Void

void表示没有任何类型。void类型只能赋值为null或undefined。

当一个函数没有返回值或者返回值是undefined时,他的返回值类型可以用void。

function warnUser(): void {
  console.log("This is my warning message");
}



Never

never类型表示永不存在的值的类型;如:

1 函数执行时抛出异常

2 函数中的代码无限循环,到不了返回值那一步

never是任何类型的子类型,可以赋值给任何类型。

// 异常
function fn(msg: string): never { 
  throw new Error(msg)
}

// 死循环
function fn(): never { 
  while (true) {}
}



Object

定义object对象类型的方式:

1 直接定义类型为object

2 将对象内属性的类型都逐个定义

let obj: object = { name: 'lin', age: 18 };
let person: { 
  age: number, name: string 
} = { age: 12, name: 'jack' };



类型扩展



类型断言

当绝对确认一个实体的类型时使用,使用方式有两种;

1.实体 as 类型(支持JSX)

2.<类型>实体

let someValue: any = "this is a string";
let strLength1: number = (someValue as string).length;
or
let strLength2: number = (<string>someValue).length;



类型别名

当定义的类型名过长或复杂时可以定义个别名,方便书写。

type s = string | number;
const str: s = 'jack';

也可以写成对象形式,定义具体的变量类型

type A = { name: string }



其他常用类型



联合类型

联合类型是由两个及以上种类型组成的类型,表示变量可以是其中的任意一种类型;且只能访问他们共有的方法和属性。

let timer: number | string | null = 1;



内置类型

ECMAScript的内置对象

const nums: Array<number> = [1,2,3]
const date: Date = new Date()
const err: Error = new Error('Error!');
const reg: RegExp = /abc/;



字面量类型

用type定义成一个字面量然后使用,能够减少代码量,降低耦合性。

type ButtonSize = 'mini' | 'small' | 'normal' | 'large'
const btnSize: ButtonSize = 'small'



函数



创建

ts可以创建有名字的函数和匿名函数。

function add(x: number, y: number): number {
  return x + y;
}

let myAdd = function (x: number, y: number): number {
  return x + y;
};

let myAdd1: (x: number, y: number) => number =
  function (x: number, y: number): number {
    return x + y;
  };



传参



已知参数个数

传递给一个函数的参数个数必须与函数期望的参数个数一致,不一致会报错。

function buildName(firstName: string, lastName: string) {
  return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error
let result3 = buildName("Bob", "Adams");        

如果参数可传可不传,可以在参数名后加 ? ,实现参数可选功能,没传默认是undefined。

可选参数必须放在必须参数后面。

function buildName(firstName: string, lastName?: string) {
  if (lastName)
    return firstName + " " + lastName;
  else
    return firstName;
}
let result1 = buildName("Bob");                  
let result2 = buildName("Bob", "Adams", "Sr."); // error, 应有 0-2 个参数,但获得 3 个。
let result3 = buildName("Bob", "Adams");         

如果是带默认值的参数放在必须参数前,要求必须明确的传入undefined值获得默认值。

function buildName(firstName = "Will", lastName: string) {
    return firstName + " " + lastName;
}
let result4 = buildName(undefined, "Adams");



未知参数个数

如果传参个数不确定,可以把所有参数收集到一个变量中。

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");



泛型

泛型的关键目的是给成员之间提供有意义的约束。成员可以是:

1 类的实例成员

2 类的方法

3 函数参数

4 函数返回值

使用泛型可以创建可重用的通用组件,一个组件可以支持多种类型的数据,在定义的时候不预先指定具体的类型,而是在使用的时候再指定。不同于any,他不会丢失信息,传入和返回类型相同。



定义泛型函数

给函数添加类型变量T(type),可以捕获到用户传入的类型;并使用T作为返回值类型。

其他常见泛型变量:

1 K(Key):表示对象中的键类型;

2 V(Value):表示对象中的值类型;

3 E(Element):表示元素类型。

function identity<T>(arg: T): T {
    return arg;
}



使用泛型函数

使用泛型函数的方法有两种

1 传入所有参数和参数类型

2 传入参数,不传类型,编译器会根据传入的参数自动确定T的类型

let output = identity<string>("myString"); 
let output = identity("myString"); 



泛型接口

interface GenericIdentityFn {
  <T>(arg: T): T;
}

function identity<T>(arg: T): T {
  return arg;
}

let myIdentity: GenericIdentityFn = identity;

返回两种类型的对象

// 创建一个Identities 接口
interface Identities<V, M> {
  value: V,
  message: M
}
// 将 Identities 接口作为 identity 函数的返回类型
function identity<T, U> (value: T, message: U): Identities<T, U> {
  console.log(value + ": " + typeof (value));
  console.log(message + ": " + typeof (message));
  let identities: Identities<T, U> = {
    value,
    message
  };
  return identities;
}

console.log(identity(68, "Semlinker"));
// 68: number
// Semlinker: string
// {value: 68, message: "Semlinker"}



泛型类

泛型类与泛型接口类似。 泛型类使用(<T, …>)括起泛型类型,定义多个类型变量,跟在类名后面。

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };



泛型约束

用于限制每个类型变量接受的类型数量。

如要求类型变量上对应的类型存在某些属性(length,push),在没有明确T类型时是不能使用length属性的。

function identity<T>(arg: T): T {
  console.log(arg.length); // Error
  return arg;
}

可以让类型变量extends一个含有所需属性(length)的接口(Length),可以获取到length属性。

但是如果调用identiy时传入参数不带有length属性会报错。

interface Length {
  length: number;
}
// 也可以使用多种约束类型
// <T extends Length, Type2, Type3, ...>
function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}



版权声明:本文为luobo2345原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。