⚡TS内功心法-学完感觉我可以啦
前言
我JS用的很顺手,为什么要用TS呢? 这不是会白白多了很多工作量吗???
相信很多小伙伴都有这个疑问,翻了网上大神的回答再结合工作中的经验,主要有以下四点:
-
面向项目:
-
TS - 面向解决大型项目,架构设计以及代码维护有一定的优势
- 比如某个函数的传参定义为
number
, 函数内部的逻辑也是基于number
类型开发的,这时候其他人传个string
,函数内部逻辑就会出错,TS
可以从起点就规避这种场景
- 比如某个函数的传参定义为
-
JS - 脚本化语言,简单场景且轻量化
-
-
自主检测
- TS -
编译时
检测,主动发现并纠正错误 - JS -
运行时
报错
- TS -
-
类型规范上
- TS - 弱类型,支持动态和静态类型检测
- JS - 弱类型,无静态类型选项
-
运行流程
- TS - 依赖编译,依靠编译打包实时生成浏览器可以运行的js
- JS - 可直接在浏览器中运行
其实,以上都是次要的,主要现在很多公司的岗位都要求会
TS
,哈哈~~~
本篇参考了一些大佬的真传干货,汇总提炼出的TS
方面的知识点,看完可以上手常规的TS项目啦~~~
正文开始
1,定义变量
五种基本类型
都在下面啦~~~
给变量定义了什么类型,值就要是什么类型,要进行统一的
let a: number = 1; //number小写
let b: string = "1";
let c: undefined = undefined;
let d: null = null;
let e: boolean = true;
如果不按照规范写的话,会报如下的错误:
2,定义数组
定义数组类型的两种方式
//普通数组
let arr1: number[] = [1, 1];
let arr2: Array<number> = [1, 1];
定义联合类型
,让数组的成员可以存在多种类型
// 联合类型(和元组的达成结果一样)
let arr3: Array<number | string> = [1, "1"];
3,定义元祖
数组合并了相同类型的对象,元祖合并了不同类型
的对象
let x: [number, string];
x = [5, "abc"];
let y: [number, string] = [5, "abc"];
4,定义对象
interface Phone {
name: string; //必填
size: number; //必填
color?: string; //选填
action(): void; //必填
[propName: string]: any; //任意属性
}
let obj: Phone = {
name: "诺基亚",
size: 100,
action: function () {},
};
let obj1: Phone = {
name: "诺基亚",
size: 100,
action: function () {},
color: "blue",
other: "测试",
};
4.1, [propName: string]: any;的使用场景
如果有很多参数定义,但是你不想全部都定义,那你就是可以定义几个主要的,其他用这个表示就行了。
还在疑惑为啥这里不解释下interface
的使用场景吗,下文有专门的区域
讲解的~~~
5,定义函数
5.1,函数入参的声明
// 第一种
function fuc1(a: string, b: string) {
return ["1", "2"];
}
fuc1('a', 'b')
// 第二种
interface MyParams {
x: number;
y: number;
}
function fuc2(params: MyParams) {
return ["1", "2"];
}
fuc2({x: 1, y: 2})
5.2,函数检查类型
场景:项目有一个公共函数传参类型(SearchFun),后续的函数继承这个接口后,都得按照这个规范来
interface SearchFun {
(a: number, b: number): boolean;
}
let fuc6: SearchFun = function (c: number, d: number): boolean {
return c > d;
};
fuc6(1, 2);
如果没按规则来的场景如下:
5.3,函数出参的声明
声明出参为字符串(): string
function fuc2(a: number | string, b: number): string {
return a + "" + b;
}
fuc2('1', 2)
6,定义类
- public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的;
- private 修饰的属性或方法是私有的,不能在声明它的类的外部访问;
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的;
- static 不需要实例化 调用类,直接调用
class Cat2 {
name: string;
color: string;
constructor(name, color) {
this.name = name;
this.color = color;
}
static eat() {
return "吃";
}
}
var c2 = new Cat2("黑猫", "黑");
c2.name;
Cat2.eat();
7,interface和type的异同
7.1,先来两句实际工作中的使用诀窍
:
-
写法类似,通常用 interface,其他无法满足需求的情况下用 type。
-
type不会声明合并,interface会声明合并。
7.2,异同之处:
type
-
可以描述对象和函数
-
可以被继承
-
可以声明基本类型,联合类型,交叉类型,元组
-
不会声明合并(重复声明会报错提示)
-
不会有索引签名问题
interface
-
可以描述对象和函数
-
可以被继承
-
不可以声明基本类型,联合类型,交叉类型,元组
-
会声明合并(声明过的接口可以再次声明,进行合并)
-
有索引签名问题
type
声明基本类型
,联合类型
,交叉类型
,数组
,元组
的案例也给小伙伴放下面了
type userName = string; // 基本类型
type userId = string | number; // 联合类型
type arr = number[]; // 数组
// 元组
type tuple = [string, boolean];
let tupleType: tuple;
tupleType = ["cluo", true];
// 对象类型
type Person = {
id: userId; // 可以使用定义类型
name: userName;
};
const user: Person = {
id: "901",
name: "椿"
};
// 泛型
type Tree<T> = { value: T };
7.3,声明合并
声明合并指的是可以声明多次,重名的会合并
interface Person { name: string }
interface Person { age: number }
let user: Person = {
name: "cluo",
age: 666,
};
喝口水,休息一会吧~~~
8,泛型
定义的时候不指定类型,用的时候指定类型
function fuc3<T>(a: T, b: T): T[] {
return [a, b];
}
fuc3<number>(1, 2);
// 或者
fuc3<string>('a', 'b');
上面的代码片段中,fuc3<T>
中的T
表示任意类型,供入参和出参使用。
用的时候可指定number
或者 string
,函数的入参和出参类型也会相应改变。
8.1,不同类型的函数
function fuc4<T>(a: T, b: T): string {
return a + "" + b + c;
}
fuc4<number | string>(1, "2");
8.2,泛型约束
泛型不支持number,因为number没有length
错误的
function f4<T>(arg: T): T {
console.log(arg.length); // 错误: T不存在length属性 比如数值等
}
f4<number>(123)
正确的
interface LengthN {
length: number;
}
function myHello<T extends LengthN>(arg: T): T {
//约束了传参必须包含length属性的类型
// console.log(arg.length);
return arg;
}
myHello<string>("123");
9,enum - 枚举
9.1,数字类枚举
不赋值就是数字类枚举,默认从零开始,依次递增
enum Score {
BAD, // 0
NG, // 1
GOOD, // 2
PERFECT, // 3
}
let score:Score = Score.BAD;
9.2,字符串枚举
enum Score {
BAD = 'BAD',
NG = 'NG',
GOOD = 'GOOD',
PERFECT = 'PERFECT',
}
9.3,反向映射
指的是取值的时候,因为做了双向映射,所以 Score[0]
的值是 BAD
enum Score {
BAD,
NG,
GOOD,
PERFECT,
}
let scoName = Score[0]; // BAD
let scoVal = Score['BAD']; // 0
9.4,异构
enmu Enum {
A, // 0
B, // 1
C = 'C',
D = 'D',
E = 6,
F, // 7 逐个递增,上面一个是数字类型6,所以它是7
}
JS实现异构枚举
let Enum;
(function(Enum) {
// 正向
Enum['A'] = 0;
Enum['B'] = 1;
Enum['C'] = 'C';
Enum['D'] = 'D';
Enum['E'] = 6;
Enum['F'] = 7;
// 逆向
Enum[0] = 'A';
Enum[1] = 'B';
Enum[6] = 'E';
Enum[7] = 'F';
})(Enum || (Enum = {}))
10,类型断言
使用场景:通常发生在你比TS更知道某个值的类型
这时,就可以使用类型断言解决TS报错。
- 尖括号写法
let someValue:any = 'this is a string';
let strLength:number = (<string>someValue).length;
- as写法
let someValue:any = 'this is a string';
let strLength:number = (someValue as string).length;
下面的padding
根据不同场景可以为number
可以为string
,但是在下面的场景,我们自己知道肯定为number
function padLeft(value: string, padding: string | number) {
// 报错: Operator '+' cannot be applied to
// types 'string | number' and 'number'
return Array(padding + 1).join(" ") + value;
}
改为
function padLeft(value: string, padding: string | number) {
return Array(padding as number + 1).join(" ") + value;
}
11,类型守卫
书接上文,有些场景as写的过多未免有些劳累。于是,有了类型守卫
主要有三种
- typeof: 用于判断 "number","string","boolean"或 "symbol" 四种类型.
- instanceof : 用于判断一个实例是否属于某个类
- in: 用于判断一个属性/方法是否属于某个对象
function padLeft(value: string, padding: string | number) {
console.log((padding as number) + 3);
console.log((padding as number) + 2);
console.log((padding as number) + 5);
return Array((padding as number) + 1).join(' ') + value;
}
11.1,用typeof
可以改为如下:
function padLeft(value: string, padding: string | number) {
if (typeof padding === 'number') {
console.log(padding + 3); //正常
console.log(padding + 2); //正常
console.log(padding + 5); //正常
return Array(padding + 1).join(' ') + value; //正常
}
if (typeof padding === 'string') {
return padding + value;
}
}
11.2,instanceof的使用场景
class Man {
handsome = 'handsome';
}
class Woman {
beautiful = 'beautiful';
}
function Human(arg: Man | Woman) {
if (arg instanceof Man) {
console.log(arg.handsome);
console.log(arg.beautiful); // 会报错,因为Man这个类里没有beautiful
} else {
// 这一块中一定是 Woman
console.log(arg.beautiful);
}
}
11.3,in的使用场景
interface Teacher {
name: string,
courses: string[];
}
interface Student {
name: string,
startTime: Date;
}
type Course = Teacher | Student;
function startCourse(cls:Course) {
if ('courses' in cls) {
console.log('teacher', cls.courses);
}
if ('startTime' in cls) {
console.log('student', cls.startTime);
}
}
12,any, unknown, void, never
12.1,any
绕过所有类型检查 => 类型检测和编译筛查全部失效
let anyValue:any = 123;
anyValue = 'anyValue';
anyValue = false;
let value1:boolean = anyValue;
12.2,unknown
绕过赋值检查 => 禁止更改传递
let unKnownValue:unknown;
unKnownValue = true;
unKnownValue = 123;
unKnownValue = 'unKnownValue';
let value1:unknown = unKnownValue; // OK
let value2:any = unKnownValue; // OK
let value3:boolean = unKnownValue; // 报错
12.3,void
void(与any相反) => 声明函数返回值
function voidFunction():void {
console.log('no return');
}
12.4,never
永不返回任何东西 或者 永远抛出error
function error(msg:string):never {
throw new Error(msg);
}
function longlongloop():never {
while(true) {
// ……
}
}
13,常用内置工具类型
13.1,Record
定义一个对象的 key 和 value 类型
type AnimalType = 'cat' | 'dog' | 'frog';
type AnimalDescription { name: string, title: string }
const AnimalMap: Record<AnimalType, AnimalDescription> = {
cat: { name: '猫', title: 'cat' },
dog: { name: '狗', title: 'dog' },
frog: { name: '蛙', title: 'wa' },
};
13.2,Partial
生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项
interface Foo {
name: string
age: number
}
type Bar = Partial<Foo>
// 相当于
type Bar = {
name?: string
age?: number
}
13.3,Required
生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为必选项
interface Foo {
name: string
age?: number
}
type Bar = Required<Foo>
// 相当于
type Bar = {
name: string
age: string
}
13.4,Readonly
生成一个新类型,T 中的 K 属性是只读的,K 属性是不可修改的。
interface Foo {
name: string
age: number
}
type Bar = Readonly<Foo>
// 相当于
type Bar = {
readonly name: string
readonly age: string
}
13.5,Pick
生成一个新类型,该类型拥有 T 中的 K 属性集 ; 新类型 相当于 T 与 K 的交集
白话:借别人家的粮食吃
interface Foo {
name: string;
age?: number;
gender: string;
}
type Bar = Pick<Foo, 'age' | 'gender'>
// 相当于
type Bar = {
age?: number
gender: string
}
13.6,Omit
和Pick
相反
生成一个新类型,该类型拥有 T 中除了 K 属性以外的所有属性
type Foo = {
name: string
age: number
}
type Bar = Omit<Foo, 'age'>
// 相当于
type Bar = {
name: string
}
13.7,Exclude
如果 T 是 U 的子类型则返回 never 不是则返回 T
取差集
type A = number | string | boolean
type B = number | boolean
type Foo = Exclude<A, B>
// 相当于
type Foo = string
13.8,Extract
和 Exclude 相反
取交集
type A = number | string | boolean
type B = number | boolean
type Foo = Extract<A, B>
// 相当于
type Foo = number | boolean
完结
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。