对象的类型
接口 Interfaces 一般首字母大写
interface Person {
readonly id: number; // 只读属性 不能修改
name: string;
age: number;
age?: number; // 可选
[propName: string]: any; // 任意属性
// 注意,
}
// 顺序 一般是 只读 -> 默认 -> 可选 -> 任意
let tom: Person = {
name: 'Tom',
age: 25
};
一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let tom: Person = {
name: 'Tom',
age: 25, // 报错 需要是 任意属性的子集
gender: 'male'
};
- 任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了
只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
tom.id = 89757; // 报错,第一处是在对 tom 进行赋值的时候,没有给 id 赋值。 之后再赋值就报错
数组类型
「类型 + 方括号」表示法
let arr: number[] = [1, 2, 3, 4, 5]; // 表示 是数组, 并且数组元素只能为 number 类型
联合类型和数组的结合
// arr 表示 是数组, 并且数组元素只能为 number 和 string 类型
let arr: (number | string)[] = ['1', 'adaf', 2, 3, 5]
数组泛型
let arr: Array<number> = [1, 2, 3, 4, 5];
类数组
常见的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等
// error 函数中的 arguments 这也定义会报错
let args: number[] = arguments;
// success 改成
let args: IArguments = arguments;
函数类型
函数声明
function sum(x: number, y: number): number {
return x + y;
}
函数表达式
let mySum = function (x: number, y: number): number {
return x + y;
};
- 这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum 添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
类型断言
function getLength(something: string | number): number {
// 将他断言为 string
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
- 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的:
// error
function toBoolean(something: string | number): boolean {
return <boolean>something;
}
// Type 'number' is not comparable to type 'boolean'
内置对象
ECMAScript 的内置对象
Boolean、Error、Date、RegExp 等。
DOM 和 BOM 的内置对象
Document、HTMLElement、Event、NodeList 等。
泛型
- 是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
问题
- type 和 Interfaces 的区别
React 中使用
import React, { MouseEvent, ReactNode } from 'react'
type Props = {
onClick(e: MouseEvent<HTMLElement>): void
children?: ReactNode
}
const Button = ({ onClick: handleClick, children }: Props) => (
<button onClick={handleClick}>{children}</button>
)
高阶组件的使用方式
import React from 'react';
import { Form } from 'antd';
import { FormComponentProps } from 'antd/es/form';
interface IProps extends FormComponentProps {
loading: boolean;
}
const Page: React.FC<IProps> = (props) => {
return (
<div>Page</div>
)
}
export default Form.create<IProps>()(Page)
对象类型取值
type DealWithProps = {
onClose: Function;
eventId: string;
dealTime: any;
};
// 处理
const getOnDealWith = (parameObj: DealWithProps) => () => {
// TODO 暂无接口
for (let i in parameObj) {
console.log(parameObj[i], typeof parameObj[i]);
// 报错
/**
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'DealWithProps'.
No index signature with a parameter of type 'string' was found on type 'DealWithProps'.ts(7053)
**/
}
};
Event 事件对象类型
常用 Event 事件对象类型:
• ClipboardEvent<T = Element> 剪贴板事件对象
• DragEvent<T = Element> 拖拽事件对象
• ChangeEvent<T = Element> Change 事件对象
• KeyboardEvent<T = Element> 键盘事件对象
• MouseEvent<T = Element> 鼠标事件对象
• TouchEvent<T = Element> 触摸事件对象
• WheelEvent<T = Element> 滚轮事件对象
• AnimationEvent<T = Element> 动画事件对象
• TransitionEvent<T = Element> 过渡事件对象
Promise 类型
Promise<T> 是一个泛型类型,T 泛型变量用于确定使用 then 方法时接收的第一个回调函数(onfulfilled)的参数类型。
interface IResponse<T> {
message: string,
result: T,
success: boolean,
}
async function getResponse (): Promise<IResponse<number[]>> {
return {
message: '获取成功',
result: [1, 2, 3],
success: true,
}
}
getResponse()
.then(response => {
console.log(response.result)
})
使用 react-router-dom
withRouter(ModuleLayout)
这类高阶组件的使用 方式,通常都需要继承到高阶组件 的 类型定义,如 antd Form.create 需要继承 FormComponentProps
import { FormComponentProps} from 'antd/lib/form/Form';
import { withRouter, Link, RouteComponentProps } from 'react-router-dom';
interface IProps extends RouteComponentProps<any> {
basePath: string;
menuList: IMenuItem[];
children: JSX.Element;
}
function App(props: IProps){
// 因为继承了 RouteComponentProps 所以可以获取 location
const {location} = props;
// xx
}
export default withRouter(ModuleLayout);
react 定义方法 useApi
/* eslint-disable no-param-reassign */
import { useRef, useEffect } from '@alipay/bigfish/react';
import { useImmer } from 'use-immer';
type Service<T> = () => Promise<T>;
type DependencyList = ReadonlyArray<any>;
interface Option<T> {
onSuccess?: (data: T) => void;
onError?: (errMsg: string) => void;
}
interface State {
data: any;
loading: boolean;
}
interface ResponseData<T> {
success: boolean;
data: T;
message: string;
}
export default function useApi<T>(
service: Service<ResponseData<T>>,
dependencyList: DependencyList = [],
option?: Option<T>
) {
const initialState: State = {
data: undefined,
loading: false,
};
const [state, setState] = useImmer(initialState);
const serviceId = useRef(0);
useEffect(() => {
const currentId = serviceId.current;
setState(draft => {
draft.loading = true;
draft.data = null;
});
// 异步方法
service().then(data => {
// 组件卸载不更改
if (currentId !== serviceId.current) {
return;
}
if (!data.success) {
// 错误
setState(draft => {
draft.loading = false;
draft.data = null;
});
if (option && option.onError) {
option.onError(data.message);
}
} else {
// 成功
setState(draft => {
draft.loading = false;
draft.data = data.data;
});
if (option && option.onSuccess) {
option.onSuccess(data.data);
}
}
});
return () => {
serviceId.current += 1;
};
}, dependencyList);
return [state, setState];
}
react 使用 useRef
import React, { useRef, useState, FC } from '@alipay/bigfish/react';
import { message } from 'antd';
interface IProps extends FormComponentProps {
isShow:boolean;
}
interface IUseRef extends HTMLDivElement {
getAssets: Function;
}
const CreateStep: FC<IProps> = props => {
const {
form, isShow
} = props;
const refList = useRef<IUseRef>(null);
const [current, setCurrent] = useState(1);
function setNextStep() {
const { current } = refList;
const { getAssets } = current; // 类型“IUseRef | null”上不存在属性“getAssets”
}
return <div ref={refList}>xxxx</div>;
};
export default CreateStep;
- 上面定义泛型的时候, 这个写法会报一个错误,因为 current 开始会是空的,后面 dom 挂在之后才会获取实例。
看下 useRef 源码定义, 也就是泛型的值就是 current 的结果
interface RefObject<T> {
readonly current: T | null;
}
function useRef<T>(initialValue: T|null): RefObject<T>;
- 并且 ref={ refLise } 需要传入的是 dom 元素节点,所以通过继承 HTMLDivElement 来得到 useRef 泛型。
interface IUseRef extends HTMLDivElement {
getAssets: Function;
}
所以解决的方法。
方法一: 类型断言, 但是这样的代码其实是不严警的
const { getAssets } = current as IUseRef;
方法二: 通过 if 判断,消除 current 为 null 的时候,如果不为 null 这里的类型就必定是 IUseRef 类型,所以可以取出 getAssets
let getAssets ;
if(current && current.getAssets){
getAssets = current.getAssets;
}