这是该系列最后一篇文章,核心讲解TypeScript
。
内容如下:
- 什么是TypeScript?
- 为什么使用TypeScript?
- 可以不使用TypeScrip?
- TypeScript优势
- TypeScript劣势
- TypeScript基础知识
- 给变量加约束
- 原始类型
- 联合类型
- 任意类型
- 枚举类型
- 对象类型
- 数组类型
- 给函数加约束
- 给函数加约束
- 给类加约束
- 类型断言作用
- 类型断言 vs 类型转换
- 类型断言 vs 泛型
- 给变量加约束
- 举例
1.什么是TypeScript?
TypeSciprt是JavaScript的超集,简单理解就是加了约束的JavaScript
。
JavaScript可以在哪些地方加约束呢?
无非就两个地方:变量
和函数
(类
也是函数)
2.为什么使用TypeScript?
加了约束后的JavaScript,可读性有很大的提升,将弱类型的语言变成了强类型的语言,当然就包括强类型的所有优点。
3.可以不使用TypeScript?
当然可以,只是目前越来越多的开发使用ts去写各种类库,想看其实现,学习ts是不错的选择。 其次,公司需要持续维护的前端项目,ts可以提高代码可读性,让陆陆续续离职入职的前端小伙伴们快速熟悉项目,交接接手更方便。
4.TypeScript优势
1.写通用组件类库 2.解读开源第三方ts库 3.拥于持续维护的前端项目
5.TypeScript劣势
优势说完,再来说说ts的劣势。 首先,ts需要一定的学习成本,得花时间和实践去理解其语法。 然后,ts导致项目代码量增大,编译会稍慢点,因为最后的构建部分ts最终还是会编译成js。 接着,ts比其他js项目开发周期会拉长,毕竟每个类每个函数每个参数都需要自定义。 最后,ts规范很重要,对于新手而言,如果项目参数全部用any声明,那还不如直接用js呢,切记不能滥用any。
总结一下: 1.有学习成本 2.代码增多,编译变慢、开发周期变长 3.ts如果使用不规范,还不如用js实在
6.TypeScript基础知识
1.给变量加约束
- 原始类型
- 联合类型
- 任意类型
- 枚举类型
- 对象类型
- 数组类型
1.原始类型:
//只允许赋值该类型(单个)
let str: string = 'hello';
let num: number = 1;
let bol: boolean = true;
let nul: null = null;
let un: undefined = undefined;
2.联合类型:
//只允许赋值该类型(多个)
let muchtype:string|number = '1';
muchtype = 2;
3.任意类型:
//允许赋值任意类型
//方式一:
let an: any = 'test';
//方式二:
let anyThing;
4.枚举类型:
enum ChatCmdType {
init = 0, // 课程初始化
chat = 1, // 群聊消息
addAnnounce = 2, // 发布公告
deleteAnnounce = 3, // 删除公告
startCourse = 4, // 开始上课
endCourse = 5, // 结束上课
}
5.对象类型:
用interface
声明:
interface ChatMessage {
account: string
headImg?: string
local: boolean
readonly role: number
text: string
[k: string]: any
}
用type
声明:
type ChatMessage = {
account: string
headImg?: string
local: boolean
readonly role: number
text: string
[k: string]: any
}
解释下声明的对象类型:
const message: ChatMessage = {
account: 'Tony',
local: true,
role: 1,
text: 'Hello, Tim'
}
message.role = 2 // 报错
message.link = 'http://baidu.com'
声明一个聊天对象: 1.除了头像,其他都必填 2.角色是数字类型且只读 3.可以自定义其他属性
6.数组类型:
// 定义数组每个item的类型
let arr: number[] = [1, 2, 3];
let str: string[] = ['1', '2', '3'];
let an: any[] = ['1', 2, true];
let messages: ChatMessage = [{xxx}, {xxx}, ...]
2.给函数加约束
1.给函数加约束
给函数加类型,简单理解就是给函数的参数
声明类型。
const on = (name: string, cb: (...args: any[]) => void) => {
cb(true, name, 1)
}
声明一个on函数: 1.第一个参数string 2.第二个参数回调callback函数 3.callback函数的参数类型是多个的任意类型
on('Tony', (bool, name, age) => {
console.log(bool, name, age)
})
// 输出 true Tony 1
注意cb: (...args: any[]) => void
和cb: (args: any[]) => void
的区别
2.给类加约束
严格意义上讲JavaScript是没有类的,只是在ES6时引入的一个语法糖class。
新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
。
函数是面向过程的写法,类是面向对象的写法。
class Person {
private name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public wark() {
console.log(this.name);
}
}
3.类型断言作用 1.将一个联合类型断言为其中一个类型
const muchtype:string|number = 1;
(muchtype as number).toString()
const muchtype:string|number = '1';
(muchtype as string).length
2.将任何一个类型断言为 any
const ipc = window.ipc;
会报错:Property 'ipc' does not exist on type 'Window & typeof globalThis'. TS2339
。
方案一,as:
const ipc = (window as any).ipc;
方案二,加忽略注释:
// @ts-ignore
const ipc = window.ipc;
4.类型断言 vs 类型转换
类型断言只会影响 TypeScript 编译时的类型
,类型断言语句在编译结果中会被删除:
function toBoolean(something: any): boolean {
return something as boolean;
}
toBoolean(1);
// 返回值为 1
在上面的例子中,将 something
断言为 boolean
虽然可以通过编译,但是并没有什么用,代码在编译后会变成:
function toBoolean(something) {
return something;
}
toBoolean(1);
// 返回值为 1
所以类型断言不是类型转换,它不会真的影响到变量的类型
。
若要进行类型转换,需要直接调用类型转换的方法:
function toBoolean(something: any): boolean {
return Boolean(something);
}
toBoolean(1);
// 返回值为 true
5.类型断言 vs 泛型 举个例子:
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
我们使用泛型:
function getCacheData<T>(key: string): T {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData<Cat>('tom');
tom.run();
通过给 getCacheData 函数添加了一个泛型
7.举例
最后看一个完整案例: 一个简单的react组件:
import React from 'react';
interface ChildProps {
onClick: (evt: any) => void
}
const Child: React.FC<ChildProps> = ({ onClick }) => {
return (<span style={{ cursor: 'pointer' }} onClick={onClick}>Child</span>)
}
export default Child
一个复杂的react组件:
import React, { useEffect, useRef, useCallback } from 'react';
import logo from 'logo.svg';
import { connect } from 'dva';
import { FunCompProps } from 'utils/types'
import Child from './Child';
import Child2 from './Child2';
import Context from 'hooks/useContext'
import useTitle from 'hooks/useTitle'
type Login = {
current: number
record: number
}
type Loading = {
global: boolean
models: Object
effects: { [k: string]: boolean }
}
interface LoginProps extends Login, FunCompProps {
loading: boolean
}
type LoginModel = {
login: Login
loading: Loading
}
const Login: React.FC<LoginProps> = ({ dispatch, loading, current }) => {
const preProps = useRef<number>()
const inputElement = useRef<HTMLInputElement>(null)
const lock = useRef<boolean>(false)
useTitle('Login')
useEffect(() => {
preProps.current = current
}, [current])
const change = async (type: string) => {
if (lock.current) return
const delay = (timeout: number) => new Promise((resolve) => {
setTimeout(resolve, timeout);
})
lock.current = true
await delay(2000)
await dispatch({
type: `login/${type}`
})
lock.current = false
}
const onClick = useCallback(() => {
inputElement.current?.focus()
}, [])
return (
<Context.Provider value={current}>
<div className="App" >
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{loading ? <p>Loading</p> : <p>{current} Page</p>}
<div>Child: <Child /></div>
<div>Child: <Child2 onClick={onClick} /></div>
<div className="App-operate">
<button className="App-btn" disabled={loading} onClick={() => change('addASync')}>Add</button>
<button className="App-btn" onClick={() => change('minus')}>Minus</button>
</div>
</header>
</div >
</Context.Provider>
)
}
export default connect(({ login, loading }: LoginModel) => ({
current: login.current,
loading: loading.effects['login/addASync']
}))(Login)
复杂的组件中,引入了一个FunCompProps的ts声明:
import H from 'history';
import {
Dispatch,
} from 'redux';
export interface match<Params extends { [K in keyof Params]?: string } = {}> {
params: Params;
isExact: boolean;
path: string;
url: string;
}
export interface StaticContext {
statusCode?: number;
}
export interface FunCompProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S = H.LocationState
> {
dispatch: Dispatch<any>
history: H.History<S>;
location: H.Location<S>;
match: match<Params>;
staticContext?: C;
}
最后总结: TypeScript并非适用于任何项目,知道即可。 TS可以当成装逼神器,如果JS语法写厌倦了,想试试新的语法,TS还是不错的选择哈。