ts基础
符号
?可选类型:位置在变量或属性名后,表示该值可写可不写 - 在变量后 age?.length { age?:number }
: 后接类型限制 - 变量后 num : number
| 联合类型,优先级较低,使用时配合括号使用,两边接数据类型 - 在类型之间 number | string
! 非空断言,表示断定!前面的变量不为空 - 表达式后 localStorage.getItem(token)!
& 并集 交叉 将类型合拼 需要注意冲突问题,可以使用Omit解决
原始数据类型
形式:变量名:数据类型
let num: number = 1030;
let str: string = 'string';
let bool: boolean = true;
let un: undefined = undefined;
let nu: null = null;
数组类型
形式:变量名:数据类型[]
// 形式1
let arr1: number[] = [1, 2, 3];
// 形式2
let arr2: (number | string)[] = [1, 2, 3, 'abc'];
// 形式3 - 配合自定义类型
type ArrNS = (number | string)[];
let arr3: ArrNS = [1, 2, 3, 'abc'];
## 函数类型 - void表示没有返回值
参数数量确定
// 形式1
function sum(n1: number, n2: number): number {
return n1 + n2;
}
// 形式2 - 配合类型别名
type Fn = (n1: number, n2: number) => number;
const add: Fn = (n1, n2) => {
return n1 + n2;
};
// 形式3
const fn = (n1:number,n2:number):number =>{
return n1 + n2
}
参数数量不确定 + 给定初始值
function sum(n1: number, n2: number = 2, n3?: number): number {
return n1 + n2 + n3;
}
对象类型
const user: { // 花括号中即为对象类型限制
name: string;
age: number;
} = {
name: 'iceahh',
age: 23,
};
配合接口使用或类型别名
interface Obj {
name: string;
age: number;
sayHi(): void; // 冒号或箭头后面表示返回值
log: (n1: number, n2: number) => number;
}
type Obj = {
name: string;
age: number;
sayHi(): void; // 冒号或箭头后面表示返回值
log: (n1: number, n2: number) => number;
}
const obj: Obj = {
name: 'iceahh',
age: 23,
sayHi() {
console.log('hi');
},
log: (n1, n2) => {
return n1 + n2;
},
};
注意:对象中的属性不能比接口中对应的类型少,但是可以多于接口中的类型
对象类型不确定时,使用 可选属性?
interface Obj {
name?: string;
age?: number;
sayHi?(): void; // 冒号或箭头后面表示返回值
log?: (n1: number, n2: number) => number;
}
可选属性符号写在属性名或变量名的后面
元组
可以指定数据的类型和数量 -- 数组形式
从类型和数量上同时限制了数据
let arr:[number,number] = [1,2]
function sum (n1:number,n2:number):[number,number] {
return [n1,n2]
}
接口interface
用来指定对象的类型
interface IPerson {
name:string
age:number
sayHi:()=>void
sayHello():void
}
类的继承 - 使用 extends 限制
interface IPoint2d {
x:number
y:number
}
interface IPoint3d extends IPoint2d{
z:number
}
type类型别名
功能比interface更强大,不仅可以指定对象的类型限制,也可以指定其他类型的限制
type Person = {
name?:string
age?:number
sayHi?():void
sayHello?:()=>void
}
const user:Person = {
name:"z3"
}
type Fn = (n1:number,n2:number)=>number
let fn:Fn = (n1,n2)=>{
return n1+n2
}
类型推论
当明确初次具有赋值操作的时候,可以通过类型推论自动确定类型
- 声明变量并初始化时
- 函数拥有返回值时
- 泛型
字面量类型
指定值-常量
值和类型是用一个
let num:1 = 1 // 指定num的值只能是1
配合联合类型使用
type actionType = 'ADD_COUNT'|'SUB_COUNT'
可以指定可选值有哪些
枚举enum
不常用,可以使用字面量+联合类型代替
enum Direction {
up,
down,
left,
right,
}
console.log(Direction.up) // 需要这样调用
enum Sex {
man = 1,
women = 2
} // 值可以修改
function send(sex:Sex):void{
console.log(sex)
}
send(Sex.man) ==> send(1)
类型断言
指定所选dom的类型,常配合操作dom时使用
const alink = document.getElementById('link') as HTMLAnchorElement
typeof
可以快速的到一个数据的类型,从而能根据此数据的类型来作为限制标准
let obj = {
name: 'z3',
age: 29,
};
//得到和obj相同的类型限制
type Obj = typeof obj;
unknown
未知类型,可以配合typeof进行类型收窄
let num:unknow = 1
if(typeof num === "string"){
console.log(num.length)
}
高级数据类型
泛型
类似变量,在调用的时候指定具体类型,实现多种类型的复用
const fn<Type> = (n1:Type):Type => {
return n1
}
fn<number>(3)
fn<string>('abc')
约束泛型 - 通过extends限制泛型
interface Ilength {
length:number
}
function getE<T extends Ilength>(n1:T):T{ // 对参数的类型进行了限制,必须拥有length属性
return n1
}
多个类型限制
keyof表示是对象类型T的键名T[K],而extends表示继承于对象类型T的键名群
function fn<T,K>(n1:T,n2:K){
}
function fn<T extends object,K extends keyof T>(n1:T,n2:K){
}
接口泛型约束
interface MyArr<T> {
length:number,
push(n1:T):void,
pop:(n1:T) =>void
}
泛型工具
Partial - 将属性设置为可选属性
Readonly - 将属性设置为可读属性
Pick - 选择几个属性
Omit - 排除几个属性
ReturnType - 获取函数类型的返回值的类型,常配合typeof使用 ----- useSelector需要用到该工具
typeof可以获得某个值的具体类型
function fn(n1:number,n2:number):number{
return n1 + n2
}
type FnReturn = RetrunType<typeof fn> // 通过ReturnType和typeof的配合使用即可得到fn的返回值类型
索引签名类型
可以指定属性的类型 - number 或 string
interface Demo<T> {
[key:number]:T
} // 不能在其中配合其他形式
索引查询类型
类似获取对象属性时使用方括号
type User = {
name:string
age:number
}
type A = User['name'] // 获取类型User中的name的类型
ts文件类型
d.ts文件 - 一般配合js文件使用
类型声明文件,可以指定js或ts文件中数据的类型,其中只能写类型声明,且可以配合declare使用,将文件中let const等非直接定义类型的关键字使用declare
declare let num:number
declare const p:string
type Position = {
x:number
y:number
}
declare let position:Position
function add(n1:number,n2:number):void {}
export default {num , p ,position , add}
// 1.使用let const 等js关键字确定类型时,需要在前面添加declare,
// 而支持ts的类型不需要添加declare
// 2.导出时,导出的并非类型,因为js中无法使用,而是导出变量
// 3.写类型限制时,无须赋值,只用限定类型,如函数,不需要写完整形式
ts文件 - 既可以写类型,也可以写js语句
tsx文件 - 是用来写组件的
react和ts
useRef
配合ts使用useRef时
- 常配合非空断言使用
- 在创建ref时通过泛型指定类型
const imgRef = useRef<HTMLImageElement>(null);
console.log(imgRef.current!.src);
useState
需要添加泛型限制
const [num, setNum] = useState<number>(0)
useLocation
需要使用泛型,根据源码,指定的泛型会作用在state上
举例代码
const location = useLocation<{ from: string }>();
const pathname = location.state ? location.state.from : '/home';
useSelector - 找到state参数和函数返回值的类型
useSelector参数为函数,其中参数state的类型为 store.getState() 的返回值
- useSelector泛型有两个参数
a. 参数1:指定的是state的类型
b. 参数2:指定的是返回值的类型
store.ts 中获取 RootState ==> useSelector中函数参数的参数的类型
export type RootState = ReturnType<typeof store.getState>;
在useSelector中指定state的类型
const user = useSelector<RootState, User>((state) => state.profile.user);
// RootState ==> state User ==> 函数返回值,该User从reducer文件中导出
// 推荐方式2
const user = useSelector((state:RootState) => state.profile.user);
// 可以不指定User类型,因为依据RootState等能推断出其类型
依据原js标签封装组件技巧
以input为例
- 使用interface
interface Props extends InputHTMLAttributes<HTMLInputElement> {
onExtraClick?: () => void;
extra?: string;
className?: string;
}
- 使用type配合Omit和&
type Props = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className'> & {
onExtraClick?: () => void;
extra?: string;
className?: string;
};
使用技巧:如何查看原生标签
查看e事件对象的类型
技巧
鉴权路由 PrivateRoute
import { getTokenKey } from '@/utils/storage';
import React from 'react';
import { Redirect, Route, RouteProps, useLocation } from 'react-router-dom';
interface Props extends RouteProps {
component: any;
}
export default function AuthRoute({ component: Component, ...rest }: Props) {
const location = useLocation();
return (
<div>
<Route
{...rest}
render={() => {
if (getTokenKey().token) {
return <Component></Component>;
} else {
return (
<Redirect
to={{
pathname: '/login',
state: { from: location.pathname },
}}
></Redirect>
);
}
}}
></Route>
</div>
);
}
定时器问题
ts会默认把setTimeout和setInterval当成nodejs环境中的定时器,而window下的定时器类型是number,推荐使用定时器的时候在前面把window补全,这样ts类型推断就会把定时器推断成number类型
异步action中的dispatch
- 如果显示类型错误,可以在dispatch后面加上泛型 异步action中的dispatch由thunk中间件提供,而thunk中间件中的dispatch继承于redux中的dispatch,因此我们指定异步action中的dispatch时,继承于redux中的dispatch
使用方法
import { Dispatch } from 'redux';
export const reqLogin = (mobile: number, code: number) => {
return async (dispatch: Dispatch) => { // 该处指定dispatch类型
const res = await request({
method: 'post',
url: '/authorizations',
data: {
mobile,
code,
},
});
dispatch(saveToken(res.data));
saveTokenKey(res.data);
};
};
reducer - ts改造
js形式
import { LOGOUT, SAVE_TOKEN } from '@/store/constants/login';
const initValue = {
token: '',
refresh_token: '',
};
export default function reducer(state = initValue, action) {
switch (action.type) {
case SAVE_TOKEN:
return action.payload;
case LOGOUT:
return {};
default:
return state;
}
}
import { LOGOUT, SAVE_TOKEN } from '@/store/constants/login';
const initValue = {
token: '',
refresh_token: '',
};
export default function reducer(state = initValue, action) {
switch (action.type) {
case SAVE_TOKEN:
return action.payload;
case LOGOUT:
return {};
default:
return state;
}
}
ts形式 - 推荐方式2
- 将Action写在一起
// import { LOGOUT, SAVE_TOKEN } from '@/store/constants/login';
type Token = { // 修改1
token: string;
refresh_token: string;
};
type Action = { // 修改2:将action写在一起
type: 'login/token' | 'login/logout';
payload: Token;
};
const initValue: Token = {
token: '',
refresh_token: '',
};
export default function reducer(state = initValue, action: Action) { // 添加类型限制
switch (action.type) {
case 'login/token':
return action.payload;
case 'login/logout':
return {} as Token; // 修改3 该处state可能为空对象,不符合Token类型的限制,添加类型断言
default:
return state;
}
}
- 将Action分小模块写 推荐
type Action =
| {
type: 'login/token';
payload: Token;
}
| {
type: 'login/logout';
payload: null;
};
给了initialValue但初始值不想写全 - 类型断言
type initialValue = {
user: {
art_count: number;
fans_count: number;
follow_count: number;
id: string;
like_count: number;
name: string;
photo: string;
};
profile: {
id: string;
photo: string;
name: string;
mobile: string;
gender: number;
birthday: string;
intro: string;
};
};
const initValue: initialValue = {
user: {},
profile: {},
} as initialValue // 使用类型断言
initialValue拆开写
将reducer中state保存的类型分开写类型限制,再糅合到initialValue中 -- 主要方便action
改造前
type initialValue = {
user: {
art_count: number;
fans_count: number;
follow_count: number;
id: string;
like_count: number;
name: string;
photo: string;
};
profile: {
id: string;
photo: string;
name: string;
mobile: string;
gender: number;
birthday: string;
intro: string;
};
};
type Action =
| {
type: 'profile/userinfo';
payload: {
art_count: number;
fans_count: number;
follow_count: number;
id: string;
like_count: number;
name: string;
photo: string;
};
}
| {
type: 'profile/profile';
payload: {
id: string;
photo: string;
name: string;
mobile: string;
gender: number;
birthday: string;
intro: string;
};
};
const initValue: initialValue = {
user: {},
profile: {},
} as initialValue
改造后
type User = {
art_count: number;
fans_count: number;
follow_count: number;
id: string;
like_count: number;
name: string;
photo: string;
};
type Profile = {
id: string;
photo: string;
name: string;
mobile: string;
gender: number;
birthday: string;
intro: string;
};
type initialValue = {
user: User;
profile: Profile;
};
type Action =
| {
type: 'profile/userinfo';
payload: User;
}
| {
type: 'profile/profile';
payload: Profile;
};
const initValue: initialValue = {
user: {},
profile: {},
} as initialValue;
问题解决:useSelector时,显示never类型
问题描述:reducer修改为ts文件时,页面文件为js文件,此时能够正常运行,将页面文件改为tsx文件时,发现了问题,其实之前问题就存在,只是因为没有改为tsx所以没有显示出来:使用useSelector获取该文件对应state中的数据时,显示为never类型
原因是reducer中的类型写得太乱了,然后就把数据重新写了一遍,学到了
- action的类型和数据的类型是不同的
a. action中的数据类型是action文件传递过来的类型
b. 数据的类型是保存在state中的类型
- 对类型可以分解的地方做到有度分解,这样使用时更方便
问题解决:在request文件中saveToken,dispatch时报错
解决方法
- 给action添加返回值类型
import { LoginAction } from '../reducers/login'; // 导入reducer中的action
export const saveToken = (payload: Token): LoginAction => { // 指定函数返回值类型即可
return {
type: 'login/token',
payload,
};
};
- 使用类型断言
export const saveToken = (payload: Token) => {
return {
type: 'login/token' as const, // 此处指定为字面量类型
payload,
};
};
RootThunkAction
在store.ts文件夹暴露
import {Anyaction} from "redux"
import {thunkAction} from "thunk"
// R:thunk的action返回值类型 =》void Promise<void>
// S:需要指定getState的返回值类型
// E:extra:额外参数 any
// A:需要指定Action的类型 Anyaction
export type RootThunkAction = thunkAction<Promise<void>,RootState,unknown,Anyaction>