基础ts
1 普通枚举
enum Gender {
GIRL = "d",
BOY = "e",
}
console.log(Gender.GIRL);
编译成
var Gender;
(function (Gender) {
Gender[Gender["GIRL"] = 'd'] = "GIRL"; // Gender[d] = GIRL Gender['GIRL'] = 'd'(默认是0)
Gender[Gender["BOY"] = 'e'] = "BOY";
})(Gender || (Gender = {}));
console.log(Gender.GIRL);
2 常量枚举
const enum Colors {
RED = "d",
BLUE = "e",
}
编译成
var myColor = [d /* RED */, e /* BLUE */];,默认是0,1
3 null/undefined是其他类型的子类型 可通过tsconfig.json中开启 strict为true,或者strictNullChecks为true,会开启严格的检查。
4 never 永远不会出现的值
不会返回的函数的返回值,或者无法到达, 没有可访问的终结点,如
function error(message: string): never {
// while(true){} //死循环也可以
throw new Error(message);
}
function Test(x: number | string) {
if (typeof x === "number") {
console.log(x);
} else if (typeof x === "string") {
console.log(x);
} else {
console.log(x); //never类型
}
}
5 函数定义
interface Props {
(name: string): void;
}
type Props2 = (name: string) => void;
const hello1: Props = () => {};
const hello2: Props2 = () => {};
函数重载
函数重载,同样一个函数,可以传不同的参数,比较有名的例子就是redux的compose,用了七个重载
function TEST(num: string, num2: string): void;
function TEST(num: number, num2: number): void; // 函数声明
function TEST(num: boolean, num2: number, num3: string): void;
function TEST(num): void {
console.log(num);
}
6 类
class Person {
myName: string;
getName(): void {
this.myName = "123";
}
转为es5就是:
// 转为es5就是Object.defineProperty
get name() {
return this.myName;
}
set name(str: string) {
this.myName = str;
}
// public公开的,自己,自己的子类都能访问 实例能访问
//protected,保护的,自己,自己的字类可以访问,实例不能访问
//private: 私有的,只能由自己继承 实例不能访问
// static 静态属性,是类上的属性,类也是一个对象,实例不能访问,相当于 Person.str4 = '123';
static str4 = '123'
public str: string = "123";
readonly dd: string = "32";
private str2: string = "23";
protected str3: string = "33";
extends继承的原理是:
function Temp(){this.constructor = Son}
Temp.prototype = Father.prototype;
Son.prototype = new Temp()
也相当于
Son.prototype = Object.create(Father.prototype) Son.prototype.constructor = Son
就是es5的组合继承,
7 装饰器
装饰器是一个函数,用来接收装饰的内容,改造它,使他强化。
8 抽象类
无法创建抽象类的实例,只能被继承
抽象方法不能在抽象类中实现,只能在字类中实现,且必须实现(定义抽象方法只能出现在抽象类中)
abstract class Person { // abstract关键字
abstract add()
}
class Son extends Person {
add() {
console.log('123');
}
}
9 接口
接口可以在面向对象编程中表示为“行为的抽象”,另外可以用来描述对象的形状。
接口就是把一些类*有的属性和方法抽象出来,可以用来约束实现此接口的类
一个类可以继承另一个类并实现多个接口
接口像插件一样是用来增强类的,而抽象类是具体类的抽象概念
一个类可以实现多个接口,一个接口也可以被多个类实现,但一个类可以多个子类,但只能有一个父类
// 行为抽象,类的行为必须实现跟接口定义一样
interface Speakable {
speak(): void;
}
// 定义类的接口
// 类型 "Person" 中缺少属性 "speak",但类型 "Speakable" 中需要该属性
class Person implements Speakable {
speak() {
console.log("aaa");
}
}
类定义接口使用implements关键字。
接口继承extends,跟自动合并效果差不多
interface A {
a(): void;
}
interface B extends A {
b(): void;
}
相当于:
interface C {
a(): void;
b(): void;
}
可索引接口
// 对数组和对象进行约束
interface User {
[index: number]: string;
}
const a: User = {
1: "23",
};
// 数组也算一个对象,键是索引,值是数据
const b: User = ["1", "2"];
10 泛型
泛型就是一个变量;
默认泛型,传T就替代了String,不传的话使用默认值。
function A<T = string>(v: T) {
const a = (a: T) => {
return a;
};
return a;
}
泛型还有一个特定,不指定T的类型,通过ts推断出入参v的类型赋值给T,然后内部就可以使用了。
const a = A(1); //A(1)返回 const a: (a: number) => number
泛型约束性:
interface LengthWise {
length: number;
}
interface Test {
length: number;
a: string;
}
function DD<T extends LengthWise>(val?: T) {
return val?.length;
}
// ts的兼容性,对于属性这种,只要包含,比如Test包含了Length,就可以。只可以多,不可以少,extends不是es6继承的意思而是约束
DD<Test>();
ts有个特殊的点,extend不是继承的意思而是约束,只要传入的T,包含了lengthWise的所有属性,就判定成功。
案例, compose的实现
// compose(a,b,c)(1) === a(b(c(1))),
// compose的实现,函数重载,支持不同的入参
type Func<T extends any[], R> = (...a: T) => R; // 每个函数的定义
/**zero function */
function compose(): <T>(a: T) => T;
/**one function */
function compose<F extends Function>(f: F): F;
/**two function compose(a,b)(1) === a(b(1)) */
function compose<A, T extends any[], R>(
f1: (a:A) => R, // a()的入参a是类型A,也就是b的返回值。而a的返回值R,就作为最后返回的值
f2: (...a: T) => A // b
): (...a: T) => R; //最终的返回值是一个可以接受多个入参的一个函数,返回值就是f1最终返回的值。
function compose(...arr) {
if (arr.length === 0) {
return (a) => a;
}
if (arr.length === 1) {
return arr[0];
}
}
function add(a) {
return a;
}
compose()(1);
const a = compose(add);
console.log(a);
11 兼容性:
如果传值的类型和声明的类型不匹配,ts就会进行兼容性检测,原理就是Duck-Check(鸭子检测)。
只要目标类型中声明的属性变量 在源类型中都存在,就是兼容的。 (只多不少原则)
如
interface A {
a: string;
b: string;
}
interface B {
a: string;
b: string;
c: string;
}
interface C {
b: string;
c: string;
}
function Test<T extends A>(a?: T) {}
Test<B>(); // B可以,因为B包含了A所有的类型
// 类型“C”不满足约束“A”。 类型 "C" 中缺少属性 "a",但类型 "A" 中需要该属性。
Test<C>(); // 报错
基本数据的兼容性:
-
联合类型
// 基本数据的兼容性 let num: string | number; let str: number = 1; num = str; str = num let num2: { toString(): string }; let str3 = "1"; num2 = str3; // 不能将类型“{ toString(): string; }”分配给类型“string”,str3能赋给num2,因为num2拥有的str3全都有。 // 而num2不能赋给str3,因为str3有的,num2没有全部拥有,只多不少原则。给的值只能比原本类型的多,不能少 str3 = num2; // 报错
函数的兼容性
- 参数:只少不多
ts中一切的一切的规则,都是为了安全,为了使用的时候不报错。
type Func = (a: number, b: number) => void;
let sum: Func;
const f4 = (a: number, b: number, c: number) => {};
sum = f4; // 参数,多了不行,少了可以,不传就是undefined。而多了不行,因为只接受两个。
- 返回值:只多不少
type GetPerson = () => { name: string; age: number };
let getPerson: GetPerson;
function g3() {
return { age: 12, dd: "3" };
}
// 类型 "{ age: number; dd: string; }" 中缺少属性 "name",但类型 "{ name: string; age: number; }" 中需要该属性。
getPerson = g3; //返回值只多不少,不然万一getPerson().name.length,而如果返回值没有name,就会报错了。
泛型的兼容性
泛型在判断兼容性的时候会先判断具体的类型然后再进行兼容性判断
// 接口的内容为空,就可以。
let x!: A<string>;
let c!: A<number>;
x = c;
interface B<T> {
data: T;
}
let xx!: B<string>;
let cc!: B<number>;
// 不能将类型“B<number>”分配给类型“B<string>”
xx = cc;
12 协变和逆变
A <= B 是指A是B的子类
A => B 指的是以A为参数类型,B为返回值的函数类型
返回值是协变的,参数类型是逆变的。
返回值可以传子类,而参数可以传父类。
参数是逆变可传父类,返回值是协变可以返回字类。
13 类型保护
类型保护就是一些表达式,他们在编译的时候就能通过类型信息确保某个作用域内变量的类型。
类型保护就是通过关键字判断出分支中的类型 如 typeof instanceof for in 来缩小范围
简单地说就是直接通过一些方法判断出值的类型,而不用ts推断。
// null保护
function B(b: string | null) {
if (b === null) {
return "";
}
// 对象可能为 "null",加上上面的保护后,就不报错了。
console.log(b.toString());
}
//链式判断
let d: { b?: 2 } | null = {};
console.log(d?.b);
// 原理就是d === null ? undefined : d.b
14 联合类型与交叉类型
联合类型遵循的原则是:只需满足其中一个即可。
interface A {
e: number;
f: string;
c: number;
}
interface B {
a: number;
b: string;
c: number;
}
type C = A | B; // C只需要满足其中一个A或者B即可,只多不少
如下:都可以,因为他们至少满足了其中一个
let ee: C = {c:1, a: 2, b: '3', e: 1}
let eee: C = {e: 1, f: '2', c: 3}
联合类型只能处理公共的属性:
function test(x: A | B) {
//型“A | B”上不存在公共属性“a”。
console.log(x.a);
console.log(x.c);
}
// 自定义类型保护
function test2(x: A | B): x is A {
return x.c === 2; // 返回布尔值
}
function test3(x: A | B) {
if (test2(x)) {
// (parameter) x: A 这时候的x已经判断为A了,因为test(2)为ture ,x is A成立
console.log(x.c);
} else {
// (parameter) x: B
x.c;
}
}
基础数据的联合类型:
type AA = string | number;
type BB = string | boolean;
type DD = AA|BB
// type DD = string | number | boolean,因为只需要满足A或者B就行,而string | number | boolean不管哪个都符合这个条件。
unknown
是any的安全类型,可以把任何值赋值给unknown类型
let value: unknown;
value = 2;
value = "3";
// 不管与谁联合,都属于unkonwn
type U1 = unknown | string;
交叉类型
交叉类型:遵循两个都满足的规则,乍一看范围更小了。因为需要两个都满足,如:
interface A {
a: string;
}
interface B {
b: string;
c: string;
}
type C = A & B;
let c: C;
// 与联合类型不同的是, 交叉类型需要同时满足A和B才赋值给C
c = { a: "1", b: "d", c: "2" };
还有一点:
interface DDD {
a: number
}
type DDDD = A & DDD
结果是 {a:never} 又要满足a是number,又要满足a是string,就是never
基础数据类型:
type AA = string | number;
type BB = string | boolean;
// type CC = string 因为需要同时满足AA和BB的只有string了
type CC = AA & BB;
never是unknown的子类型
//type isNever = true
type isNever = never extends unknown ? true : false; //true