STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

electron+hooks+ts实现互动直播大班课(五)

这是该系列最后一篇文章,核心讲解TypeScript

ts

内容如下:

  • 什么是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.原始类型:

1
2
3
4
5
6
//只允许赋值该类型(单个)
let str: string = 'hello';
let num: number = 1;
let bol: boolean = true;
let nul: null = null;
let un: undefined = undefined;

2.联合类型:

1
2
3
//只允许赋值该类型(多个)
let muchtype:string|number = '1';
muchtype = 2;

3.任意类型:

1
2
3
4
5
//允许赋值任意类型
//方式一:
let an: any = 'test';
//方式二:
let anyThing;

4.枚举类型:

1
2
3
4
5
6
7
8
enum ChatCmdType {
init = 0, // 课程初始化
chat = 1, // 群聊消息
addAnnounce = 2, // 发布公告
deleteAnnounce = 3, // 删除公告
startCourse = 4, // 开始上课
endCourse = 5, // 结束上课
}

5.对象类型:
interface声明:

1
2
3
4
5
6
7
8
interface ChatMessage {
account: string
headImg?: string
local: boolean
readonly role: number
text: string
[k: string]: any
}

type声明:

1
2
3
4
5
6
7
8
type ChatMessage = {
account: string
headImg?: string
local: boolean
readonly role: number
text: string
[k: string]: any
}

解释下声明的对象类型:

1
2
3
4
5
6
7
8
const message: ChatMessage = {
account: 'Tony',
local: true,
role: 1,
text: 'Hello, Tim'
}
message.role = 2 // 报错
message.link = 'http://baidu.com'

声明一个聊天对象:
1.除了头像,其他都必填
2.角色是数字类型且只读
3.可以自定义其他属性


6.数组类型:

1
2
3
4
5
// 定义数组每个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.给函数加约束
给函数加类型,简单理解就是给函数的参数声明类型。

1
2
3
const on = (name: string, cb: (...args: any[]) => void) => {
cb(true, name, 1)
}

声明一个on函数:
1.第一个参数string
2.第二个参数回调callback函数
3.callback函数的参数类型是多个的任意类型

1
2
3
4
on('Tony', (bool, name, age) => {
console.log(bool, name, age)
})
// 输出 true Tony 1

注意cb: (...args: any[]) => voidcb: (args: any[]) => void的区别


2.给类加约束
严格意义上讲JavaScript是没有类的,只是在ES6时引入的一个语法糖class。
新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
函数是面向过程的写法,类是面向对象的写法。

1
2
3
4
5
6
7
8
9
10
11
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.将一个联合类型断言为其中一个类型

1
2
3
4
5
const muchtype:string|number = 1;
(muchtype as number).toString()

const muchtype:string|number = '1';
(muchtype as string).length

2.将任何一个类型断言为 any

1
const ipc = window.ipc;

会报错:Property 'ipc' does not exist on type 'Window & typeof globalThis'. TS2339
方案一,as:

1
const ipc = (window as any).ipc;

方案二,加忽略注释:

1
2
// @ts-ignore
const ipc = window.ipc;

4.类型断言 vs 类型转换
类型断言只会影响 TypeScript 编译时的类型,类型断言语句在编译结果中会被删除:

1
2
3
4
5
6
function toBoolean(something: any): boolean {
return something as boolean;
}

toBoolean(1);
// 返回值为 1

在上面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用,代码在编译后会变成:

1
2
3
4
5
6
function toBoolean(something) {
return something;
}

toBoolean(1);
// 返回值为 1

所以类型断言不是类型转换,它不会真的影响到变量的类型
若要进行类型转换,需要直接调用类型转换的方法:

1
2
3
4
5
6
function toBoolean(something: any): boolean {
return Boolean(something);
}

toBoolean(1);
// 返回值为 true

5.类型断言 vs 泛型
举个例子:

1
2
3
4
5
6
7
8
9
10
11
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();

我们使用泛型:

1
2
3
4
5
6
7
8
9
10
11
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 函数添加了一个泛型 ,我们可以更加规范的实现对 getCacheData 返回值的约束,这也同时去除掉了代码中的 any,是最优的一个解决方案。


7.举例

最后看一个完整案例:
一个简单的react组件:

1
2
3
4
5
6
7
8
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组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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还是不错的选择哈。