TypeScript basics
-
- a data type
-
- 1 basic data type
- 2 any type
- 3 interface interface
- 4 array types
- 5 Function types
- 6 Type Assertion|Union Type|Intersection Type
- 7 built-in objects
- 8 classes
- 9-tuple
- 10 enumeration enum
- 11 Type inference | Type aliases
- 12 never
- 13 Symbol
- 14 Generics***
- 15 tsconfig.json file
- 16 namespace namespace
- 17 triple slash instruction
- 18 declaration file d.ts
- 19 Mixins
- 20 Decorator Decorator
- 21 webPack build TS project
- 22 Actual combat: TS writes publish and subscribe mode
- 23 TS advanced usage: proxy & Reflect
- 24 TS advanced usage: Partial & Pick
a data type
Basic types: Boolean, Number, String, null
, undefined
and ES6's Symbol and ES10's BigInt .
1 basic data type
1.1 string
String types are string
defined .
//普通声明
let a: string = '123'
//也可以使用es6的字符串模板
let str: string = `dddd${
a}`
1.2 number
Use number
to define number type, support hexadecimal, decimal, octal, binary.
let notANumber: number = NaN; //Nan
let num: number = 123; //普通数字
let infinityNumber: number = Infinity;//无穷大
let decimal: number = 6; //十进制
let hex: number = 0xf00d; //十六进制
let binary: number = 0b1010; //二进制
let octal: number = 0o744; //八进制
1.3 boolean
// 这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象
let createdBoolean: boolean = new Boolean(1)
//
let createdBoolean: Boolean = new Boolean(1)
let booleand: boolean = true //可以直接使用布尔值
let booleand2: boolean = Boolean(1) //也可以通过函数返回布尔值
1.4 object
const user: {
_id: number, name: string } = {
id: 1, name: 'Li'};
// ? 表示可选属性
const user: {
_id: number, name?: string } = {
id: 1, name: 'Li'};
// [propName: string]: any 任意可选属性
const user: {
_id: number, name: string, [propName: string]: any }
= {
id: 1, name: 'Li', age: 18, gender: '男'};
//特殊的 可以赋值一个函数,也不会报错
let a: object;
a:{
};
a = function () {
};
1.5 void
JavaScript has no concept of null values.
void can be used in TypeScript 表示没有任何返回值的函数
.
function voidFn():void {
// ...
}
void 类型的用法: 主要是用在不希望调用者关心函数返回值的情况下,比如通常的异步回调函数。
void can define undefined and null
let u: void = undefined;
let n: void = null;
1.6 unll and undefined
let u: undefined = undefined; // 定义 undefined
let n: null = null; // 定义 null
The difference between void and (undefined, null):
Undefined and null are all types 子类
, that is, variables of undefined and null types can be assigned to variables of other types.
//这样是没问题的
let test: null = null
let s: string = "1"
s = test // 可以
//或者这样的
let test: undefined = undefined
let s: string = "1"
s = test // 可以
But variables of void type cannot be assigned to other types.
// void 类型的变量不能赋值给其他类型,下面这样写会报错
let test: void = undefined;
let s: string = '1';
s = test; // 报错
2 any type
any type and unknown top-level type.
2.1 any
Any means that we define a variable of any type, which does not require type checking and can be switched arbitrarily.
let anys:any = 123
anys = '123'
anys = true
// 声明变量的时候没有指定任意类型,默认为 any
let anys;
anys = '123'
anys = true
**Disadvantages:** If you use any, you will lose the role of TS type detection
2.2 unknown
unknown means unknown . The unknown type introduced in TypeScript 3.0 is also considered a top type, but it is safer. As with any, 所有类型都可以分配给 unknown
.
The unknown type is more strict than any. When you want to use any, you can try to use unknown
//unknown 可以定义任何类型的值
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {
}; // OK
value = null; // OK
value = undefined; // OK
value = Symbol("type"); // OK
//这样写会报错unknow类型不能作为子类型只能作为父类型 any可以作为父类型和子类型
//unknown类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names
//这样就没问题 any类型是可以的
let names:any = '123'
let names2:string = names
//unknown可赋值对象只有unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'
aaa = bbb
difference 2
// 如果是 any 类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {
b:1}
obj.a
// 如果是 unknown 是不能调用属性和方法
let obj:unknown = {
b:1,ccc:():number=>213}
obj.b
obj.ccc()
2.3 Differences
1 unknown is stricter and safer than any.
unknown 只能作为父类,也就是可以将任意类型是变量赋值给 unknown 类型的变量,但是不能将 unknown 类型的变量赋值给其他任意类型。
any 既可以作父类又可以作子类,即可以将 any 类型的变量赋值给其他任意类型,也可以将任意类型的变量赋值给 any类型。
2 for properties and methods
unknown 类型是不能调用属性和方法的。
any 类型是可以的,而且在对象上获取一个不存在的属性也是不会报错的。
3 interface interface
3.1 what is the interface
In TypeScript, the way to define objects is to use keywords interface
(interfaces).
It can be understood as using interface to define a constraint, so that the structure of the data meets the format of the constraint.
That is to say, a special data type of multiple structures can be defined through the interface.
// 使用 interface 定义一个 Person 的类型,其中有两个为 string 的属性
interface Person {
b:string,
a:string
}
// 这里使用上面定义的 Person 接口约束
// 目前这样写是会报错的,因为使用接口约束的时候,属性的数量必须与接口定义的属性保持一致。不能多不能少
const person:Person = {
a:"213"
// 属性的数量要与接口定义的属性保持一致
// 但可以使用可选属性来解决这个问题 ?.
}
3.2 Interfaces with the same name will be merged
interface A {
name:string }
interface A {
age:number }
let x:A = {
name:'xx', age:20 }
3.3 interface can use extends for inheritance
// 定义一个 A 接口
interface A {
name:string
}
// B 接口继承 A 中定义的属性
interface B extends A{
age:number
}
// 使用 B 接口
let test:B = {
age:18,
name:"string"
}
3.4 Optional attributes using the ? operator
//可选属性的含义是该属性可以不存在,所以说这样写也是没问题的
interface Person {
a:string,
b?:string
}
const person:Person = {
a:"213"
}
3.5 Arbitrary property [propName: string]
It should be noted that once any attribute is defined, the type of both definite and optional attributes must be a subset of its type:
// 使用 [propName: string]: any 定义任意属性
interface Person {
b?:string,
a:string,
[propName: string]: any;
}
//在这个例子当中我们看到接口中并没有定义C但是并没有报错
//应为我们定义了[propName: string]: any;
//允许添加新的任意属性
const person:Person = {
a:"213",
c:"123"
}
3.6 Read-only attribute readonly
readonly 只读属性是不允许被赋值的只能读取
//这样写是会报错的
//应为a是只读的不允许重新赋值
interface Person {
b?: string,
readonly a: string,
[propName: string]: any;
}
const person: Person = {
a: "213",
c: "123"
}
person.a = 123
3.7 Add function
interface Person {
b?: string,
readonly a: string,
[propName: string]: any,
cb:()=>void
}
const person: Person = {
a: "213",
c: "123",
cb:()=>{
console.log(123)
}
}
3.8 Summary:
1 使用 interface 定一个特殊的可约束的数据类型结构
2 在使用接口约束的时候,变量的属性与接口约束的属性保持一致,不能多也不能少。
3 重名的 interface 会进行合并
4 interface 可以使用 extends 进行继承
5 可以使用 ? 操作符定义可选属性,解决第二点的问题
6 [propName: string]: any 定义任意属性
7 readonly 只读属性,不允许被赋值,只能读取
8 ()=> void 同时是可以添加函数的。
4 array types
4.1 Basic types
Array types are defined using type brackets : number[]
// 定义数组类型时需要指定类型,如数字类型、字符串类型等,以及任意类型
let arr: number[] = [1, 2, 3];
let arr2: string[] = ["1", "2"];
let arr3: any[] = [1, "2", true]; // 数组中可以存任意类型的元素
// 如果指定的特定的类型,是不允许数组中有除此之外类型的元素的
let arr: number[] = [1,2,3,'1']; // 此时会报错
arr.unshift('1'); //操作方法添加也是不允许的
4.2 Array Generics
Rule Array <type>
let arr: Array<number> = [1, 2, 3];
4.3 Representing arrays with interfaces
Generally used to describe class arrays.
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
//表示:只要索引的类型是数字时,那么值的类型必须是数字。
4.5 Multidimensional arrays
let data:number[][] = [[1,2], [3,4]];
4.6 arguments class array
function Arr(...args:any): void {
console.log(arguments)
//错误的arguments 是类数组不能这样定义
let arr:number[] = arguments
}
Arr(111, 222, 333)
function Arr(...args:any): void {
console.log(arguments)
//ts内置对象IArguments 定义
let arr:IArguments = arguments
}
Arr(111, 222, 333)
//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
5 Function types
5.1 Define function type
// 定义一个函数
const fn = (name: string, age:number): string => {
return name + age
}
// 注意,参数不能多传,也不能少传 必须按照约定的类型来
fn('张三',18)
5.2 Optional arguments to functions ?
//通过 ? 表示该参数为可选参数
const fn = (name: string, age?:number): string => {
return name + age
}
fn('张三')
5.3 Default values for function parameters
const fn = (name: string = "我是默认值"): string => {
return name
}
fn()
5.4 Interface definition function
//定义参数 num 和 num2 :后面定义返回值的类型
interface Add {
(num: number, num2: number): number
}
const fn: Add = (num: number, num2: number): number => {
return num + num2
}
fn(5, 5)
interface User{
name: string;
age: number;
}
function getUserInfo(user: User): User {
return user
}
5.5 Define remaining parameters...
const fn = (array:number[], ...items:any[]):any[] => {
console.log(array,items)
return items
}
let a:number[] = [1,2,3]
fn(a,'4','5','6')
5.6 Function overloading
Overloading means that the method name is the same, but the parameters are different, and the return type can be the same or different.
If the parameter types are different, the parameter type should be set to any.
Different number of parameters You can make different parameters optional.
function fn(params: number): void
function fn(params: string, params2: number): void
function fn(params: any, params2?: any): void {
console.log(params)
console.log(params2)
}
fn(123)
fn('123',456)
6 Type Assertion|Union Type|Intersection Type
6.1 Union types |
// 例如某些身份证号会有 X 存在,即会有数字又有字母,因此可以使用联合类型
// 使用一个竖线 | 来定义联合类型的变量
let ID: number | string = '412722xxx';
Functions use union types
const fn = (something:number | boolean):boolean => {
return !!something
}
6.2 Intersection type&
A collection of multiple types, the union object will have all members of all union types
interface People {
age: number,
height: number
}
interface Man{
sex: string
}
// 使用 & 联合类型
const user = (man: People & Man) => {
console.log(man.age)
console.log(man.height)
console.log(man.sex)
}
user({
age: 18,height: 180,sex: 'male'});
6.3 Type Assertions
Use type assertion to infer which interface the incoming value belongs to.
Syntax: 值 as 类型
or<类型>值
interface A {
run: string
}
interface B {
build: string
}
const fn = (type: A | B): string => {
return type.run
}
//这样写是有警告的应为B的接口上面是没有定义run这个属性的
interface A {
run: string
}
interface B {
build: string
}
const fn = (type: A | B): string => {
return (type as A).run
}
//可以使用类型断言来推断他传入的是A接口的值
Note: type assertion can only "cheat" the TS compiler, if there is an error, it cannot be avoided.
Conversely, misuse of type assertions can lead to runtime errors.
Use any ad-hoc assertion
//这样写会报错因为window没有abc这个东西
window.abc = 123;
//可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的
(window as any).abc = 123;
as const
is an assertion of literal values, which is different from const directly defining constants
in the case of普通类型跟直接const 声明是一样的
const names = '小满'
names = 'aa' //无法修改
let names2 = '小满' as const
names2 = 'aa' //无法修改
// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];
a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针
Type assertions are irrelevant
In the following example, asserting that something is boolean can be compiled, but it is useless and will not affect the result, because the type assertion will be deleted during compilation
function toBoolean(something: any): boolean {
return something as boolean;
}
toBoolean(1);
// 返回值为 1
7 built-in objects
There are many built-in objects in JavaScript that can be directly used as defined types in TypeScript.
7.1 ECMAScript built-in objects
Boolean、Number、String、RegExp、Date、Error
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('字符串String')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("error!")
console.log(e)
7.2 DOM and BOM built-in objects
Document、HTMLElement、Event、NodeList 等
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
document.addEventListener('click', function (e: MouseEvent) {
});
//dom元素的映射表
interface HTMLElementTagNameMap {
"a": HTMLAnchorElement;
"abbr": HTMLElement;
"address": HTMLElement;
"applet": HTMLAppletElement;
"area": HTMLAreaElement;
"article": HTMLElement;
"aside": HTMLElement;
"audio": HTMLAudioElement;
"b": HTMLElement;
"base": HTMLBaseElement;
"bdi": HTMLElement;
"bdo": HTMLElement;
"blockquote": HTMLQuoteElement;
"body": HTMLBodyElement;
"br": HTMLBRElement;
"button": HTMLButtonElement;
"canvas": HTMLCanvasElement;
"caption": HTMLTableCaptionElement;
"cite": HTMLElement;
"code": HTMLElement;
"col": HTMLTableColElement;
"colgroup": HTMLTableColElement;
"data": HTMLDataElement;
"datalist": HTMLDataListElement;
"dd": HTMLElement;
"del": HTMLModElement;
"details": HTMLDetailsElement;
"dfn": HTMLElement;
"dialog": HTMLDialogElement;
"dir": HTMLDirectoryElement;
"div": HTMLDivElement;
"dl": HTMLDListElement;
"dt": HTMLElement;
"em": HTMLElement;
"embed": HTMLEmbedElement;
"fieldset": HTMLFieldSetElement;
"figcaption": HTMLElement;
"figure": HTMLElement;
"font": HTMLFontElement;
"footer": HTMLElement;
"form": HTMLFormElement;
"frame": HTMLFrameElement;
"frameset": HTMLFrameSetElement;
"h1": HTMLHeadingElement;
"h2": HTMLHeadingElement;
"h3": HTMLHeadingElement;
"h4": HTMLHeadingElement;
"h5": HTMLHeadingElement;
"h6": HTMLHeadingElement;
"head": HTMLHeadElement;
"header": HTMLElement;
"hgroup": HTMLElement;
"hr": HTMLHRElement;
"html": HTMLHtmlElement;
"i": HTMLElement;
"iframe": HTMLIFrameElement;
"img": HTMLImageElement;
"input": HTMLInputElement;
"ins": HTMLModElement;
"kbd": HTMLElement;
"label": HTMLLabelElement;
"legend": HTMLLegendElement;
"li": HTMLLIElement;
"link": HTMLLinkElement;
"main": HTMLElement;
"map": HTMLMapElement;
"mark": HTMLElement;
"marquee": HTMLMarqueeElement;
"menu": HTMLMenuElement;
"meta": HTMLMetaElement;
"meter": HTMLMeterElement;
"nav": HTMLElement;
"noscript": HTMLElement;
"object": HTMLObjectElement;
"ol": HTMLOListElement;
"optgroup": HTMLOptGroupElement;
"option": HTMLOptionElement;
"output": HTMLOutputElement;
"p": HTMLParagraphElement;
"param": HTMLParamElement;
"picture": HTMLPictureElement;
"pre": HTMLPreElement;
"progress": HTMLProgressElement;
"q": HTMLQuoteElement;
"rp": HTMLElement;
"rt": HTMLElement;
"ruby": HTMLElement;
"s": HTMLElement;
"samp": HTMLElement;
"script": HTMLScriptElement;
"section": HTMLElement;
"select": HTMLSelectElement;
"slot": HTMLSlotElement;
"small": HTMLElement;
"source": HTMLSourceElement;
"span": HTMLSpanElement;
"strong": HTMLElement;
"style": HTMLStyleElement;
"sub": HTMLElement;
"summary": HTMLElement;
"sup": HTMLElement;
"table": HTMLTableElement;
"tbody": HTMLTableSectionElement;
"td": HTMLTableDataCellElement;
"template": HTMLTemplateElement;
"textarea": HTMLTextAreaElement;
"tfoot": HTMLTableSectionElement;
"th": HTMLTableHeaderCellElement;
"thead": HTMLTableSectionElement;
"time": HTMLTimeElement;
"title": HTMLTitleElement;
"tr": HTMLTableRowElement;
"track": HTMLTrackElement;
"u": HTMLElement;
"ul": HTMLUListElement;
"var": HTMLElement;
"video": HTMLVideoElement;
"wbr": HTMLElement;
}
8 classes
ES6 provides a way of writing closer to traditional languages, introducing the concept of class
class as an object template. Classes can be defined with the class keyword.
Basically, the ES6 class can be regarded as just a syntactic sugar, and most of its functions can be achieved by ES5.
The new class writing method just makes the object prototype writing method clearer and more like the syntax of object-oriented programming.
8.1 class definition variable
//定义类
class Person {
name: string
age: number = 0
constructor (name: string,age: number) {
this.name = name
this.age = age
}
run () {
}
}
/* 注意:
1 不允许在 constructor 定义变量,需要在 constructor 上面先声明
2 如果定义了变量不使用,也会报错的,通常给一个默认值或者进行赋值
*/
8.2 Class modifiers
There are three:
public: if not written, the default is public, public, accessible both internally and externally
private: private attribute, only internal access
protected:
class Person {
public name: string
private age: number
protected some: any
constructor (name: string, ages: number, some: any){
this.name = name
this.age = ages
this.some = some
}
}
let zs = new Person('张三', 19, 1);
zs.name // 可以访问
zs.age // 私有属性,只能在内部访问,不能在外部访问
zs.some // protected 定义的变量也是私有的,只能在内部和继承的子类中访问,不能在外部访问
class Person {
public name:string
private age:number
protected some:any
constructor (name:string,ages:number,some:any) {
this.name = name
this.age = ages
this.some = some
}
run () {
}
}
class Man extends Person{
constructor () {
super("张三",18,1)
console.log(this.some)
}
create () {
console.log(this.some)
}
}
let xiaoman = new Person('小满',18,1)
let man = new Man()
man.some
8.3 static static properties and static methods
class Person {
static nb: string
static aaa () {
return console.log(123467)
}
constructor () {
this.nb // 此时会报错的
this.aaa() // 也会报错
}
}
Person.nb // 可以
Person.aaa()// 可以
// static 静态属性,不能通过 this 去访问,只能通过类名去调用
// static 静态方法,不能通过 this 去调用,也是通过类名去调用
Note: If both functions are static , they can call each other through this .
class Person {
static nb: string
static aaa () {
return console.log(123467)
}
static bbb () {
return this.aaa()
}
constructor () {
}
}
8.4 interface definition class
interface PersonClass {
get(type: boolean): boolean
}
interface PersonClass2{
set():void,
asd:string
}
class A {
name: string
constructor() {
this.name = "123"
}
}
// 使用关键字 implements,后面跟 interface 的名字,多个用都好隔开
// 继承还是用 extends
class Person extends A implements PersonClass,PersonClass2 {
asd: string
constructor() {
super()
this.asd = '123'
}
get(type:boolean) {
return type
}
set () {
}
}
8.5 Abstract classes
Application scenario If the class you wrote is useless after instantiation, I can define it as an abstract class
Or you can also use it as a base class -> implement some methods of the base class by inheriting a derived class
Let's look at an example. The following code will report an error that the abstract class cannot be instantiated.
abstract class A {
public name:string
}
new A()
Example 2
We defined the getName abstract method in class A but for implementation
Our class B implements the abstract method defined by A. If it is not implemented, no error will be reported.
The abstract method we define must be implemented in the derived class
abstract class A {
name: string
constructor(name: string) {
this.name = name;
}
print(): string {
return this.name
}
abstract getName(): string
}
class B extends A {
constructor() {
super('小满')
}
getName(): string {
return this.name
}
}
let b = new B();
console.log(b.getName());
video case
//1. class 的基本用法 继承 和 类型约束
//2. class 的修饰符 readonly private protected public
//3. super 原理
//4. 静态方法
//5. get set
interface Options {
el: string | HTMLElement
}
interface VueCls {
init(): void
options: Options
}
interface Vnode {
tag: string
text?: string
props?: {
id?: number | string
key?: number | string | object
}
children?: Vnode[]
}
class Dom {
constructor() {
```
}
private createElement(el: string): HTMLElement {
return document.createElement(el)
}
protected setText(el: Element, text: string | null) {
el.textContent = text;
}
protected render(createElement: Vnode): HTMLElement {
const el = this.createElement(createElement.tag)
if (createElement.children && Array.isArray(createElement.children)) {
createElement.children.forEach(item => {
const child = this.render(item)
this.setText(child, item.text ?? null)
el.appendChild(child)
})
} else {
this.setText(el, createElement.text ?? null)
}
return el;
}
```
}
class Vue extends Dom implements VueCls {
options: Options
constructor(options: Options) {
super()
this.options = options;
this.init()
}
static version () {
return '1.0.0'
}
public init() {
let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el;
let data: Vnode = {
tag: "div",
props: {
id: 1,
key: 1
},
children: [
{
tag: "div",
text: "子集1",
},
{
tag: "div",
text: "子集2"
}
]
}
app?.appendChild(this.render(data))
console.log(app);
```
this.mount(app as Element)
}
```
public mount(app: Element) {
document.body.append(app)
}
}
const v = new Vue({
el: "#app"
})
9-tuple
9.1 Use of tuples
Concept: A Tuple is a collection of a fixed number of elements of different types.
If you need a fixed-size collection of values of different types, you can use tuples.
let arr:[number,string] = [1,'string']
let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]
// 元组和集合的不同之处在于:
// 元组中的元素类型可以是不同的,而且数量是固定的。
// 元组的好处在于可以把多个元素作为一个单元传递。
// 如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示。
When assigning or accessing an element with a known index, the correct type is obtained:
let arr:[number,string] = [1,'string']
arr[0].length //error
arr[1].length //success
//数字是没有length 的
Tuple types can also support custom names and become optional
let a: [x: number, y?: boolean] = [1]
9.2 Out-of-bounds elements
let arr:[number,string] = [1,'string']
arr.push(true)//error
For out-of-bounds elements, its type is restricted to the union type (that is, the type you define in the tuple)
let arr: [number, string] = [1, 'string'];
arr.push(false); // 会提示 类型Boolean的参数不能赋值给类型 string|number 的参数
Application scenarios, such as defining the data returned by Excel
let excel: [string, string, number, string][] = [
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
]
10 enumeration enum
There is no concept of enums in JS. TS can define enumerations using enum
the keyword .
10.1 Numeric enumerations
// ts 定义的枚举中每一个组员默认都是从0开始的
enum Types {
Red,
Green,
Blue
}
// 等同于
enum Types {
Red = 0,
Green = 1,
Blue = 2
}
growth type
The defined number enumeration can specify the initial value, and the rest of the group members will automatically grow.
// 例如 Red 初始化为 1,其余成员会从 1 开始自动增长。
// 即 Types 的 Green 为 2,Blue 为 3
enum Types{
Red = 1,
Green,
BLue
}
10.2 String Enumerations
Inside a string enum, each member must be initialized with a string literal, or with another string enum member.
enum Types{
Red = 'red',
Green = 'green',
BLue = 'blue'
}
// 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。
10.3 Heterogeneous enums
A heterogeneous enumeration is one that can mix string and number members.
enum Types{
No = "No",
Yes = 1,
}
10.4 Interface enumeration
// 定义一个枚举 Types
enum Types {
yyds,
dddd
}
// 定义一个接口 A,有个属性 red 值为 Types.yyds
interface A {
red:Types.yyds
}
// 声明对象的时候要遵循这个原则
let obj:A = {
red: Types.yyds
}
10.5 const enums
The enumeration declared by const will be compiled into a constant, and the enumeration of ordinary declaration will be compiled into an object.
Both let and var are not allowed to declare enums.
const enum Types{
No = "No",
Yes = 1,
}
/*
大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义
*/
10.6 Reverse mapping
It contains forward mapping (name -> value) and reverse mapping (value -> name)
Note: No reverse mapping will be generated for string enum members.
// 定义一个枚举 Enum
enum Enum {
fall
}
let a = Enum.fall;
console.log(a); //0
let nameOfA = Enum[a];
console.log(nameOfA); //fall
11 Type inference | Type aliases
11.1 Type inference
1 A variable is declared, but the type is not defined.
TS will guess when the type is not explicitly specified, and give a guessed type, which is type inference.
// 此时 TS 会推论变量 str 是 string 类型
let str = 'LXTL';
// 因此不能够再赋值给别的类型。 此时会报错,不能将 number 分配给 string
str = 123456;
2 A variable is declared without a type and without assignment.
At this time, TS will be inferred as any type, and any operation can be performed.
let xiaoxu
xiaoxu = 18
xiaoxu = true
xiaoxu = 'xiaoxu'
11.2 Type alias type
type The key is to define a name for a type, which is mostly used to conform to the type.
1 Define type aliases
// 定义一个 string 的类型为 str
type str = string
// 使用 str 类型
let s: str = 'LXBM'
2 Define function aliases
type str = () => string
let s: str = () => "我是小满"
3 Define union type aliases
type str = string | number
let s: str = 123
let s2: str = '123'
4 Define an alias for the value
type value = boolean | 0 | '213'
let s:value = true //变量s的值 只能是上面value定义的值
11.3 The difference between type and interface
1.interface可以继承 type 只能通过 & 交叉类型合并
2.type 可以定义 联合类型 和 可以使用一些操作符 interface不行
3.interface 遇到重名的会合并 type 不行
11.4 advanced usage of type
The value on the left will be a subtype of the value on the right, following the inclusion relationship of the upper and lower in the graph
type a = 1 extends number ? 1 : 0 //1
type a = 1 extends Number ? 1 : 0 //1
type a = 1 extends Object ? 1 : 0 //1
type a = 1 extends any ? 1 : 0 //1
type a = 1 extends unknow ? 1 : 0 //1
type a = 1 extends never ? 1 : 0 //0
12 never
12.1 what is never
never
Types are used in TS to represent state that should not exist.
// 返回never的函数必须存在无法达到的终点
// 因为必定抛出异常,所以 error 将不会有返回值
function error(message: string): never {
throw new Error(message);
}
// 因为存在死循环,所以 loop 将不会有返回值
function loop(): never {
while (true) {
}
}
12.2 Difference between never and void
//void类型只是没有返回值 但本身不会出错
function Void():void {
console.log();
}
//只会抛出异常没有返回值
function Never():never {
throw new Error('aaa')
}
Difference 2: When we move the mouse up, we will find that there are only void and number
never will be removed directly in the union type
type A = void | number | never
12.3 Application scenarios of never type
It can be used for bottom-up logic.
type A = 'key1' | 'key2' | 'key3'
function fn(value:A) {
switch (value) {
case "key1":
break
case "key2":
break
case "key3":
break
default:
break
}
}
For example, if another colleague needs to add a new key4, then all Switch codes must be manually found and processed, otherwise bugs may be introduced.
Since any type cannot be assigned to a variable of never
the type , when there is default
a possibility of entering the branch, the type check of TS will help us find this problem in time.
type A = 'key1' | 'key2' | 'key3' | 'key4'
function fn(value:A) {
switch (value) {
case "key1":
break
case "key2":
break
case "key3":
break
default:
//是用于场景兜底逻辑
const error:never = value;
return error
}
}
13 Symbol
Since ECMAScript 2015, symbol has become a new primitive type, just like number and string.
Values of type symbol are created through the Symbol constructor.
// 可以传递参数作为唯一标识。只支持 number 、string两种类型以及 不传参。
let sym1 = Symbol();
let sym2 = Symbol(123);
let sym3 = Symbol('key');
Symbol values are unique.
const s1 = Symbol();
const s2 = Symbol();
// s1 === s2 => false 内存地址并不同
key to use as object property
let sym = Symbol();
// 定义的 symbol 可以作为对象的属性名
let obj = {
[sym]: "value"
};
console.log(obj[sym]); // "value"
Attributes defined using symbols cannot be obtained through traversal as follows:
for in 、Object.keys 、getOwnPropertyNames、JSON.stringfy
const symbol1 = Symbol('666')
const symbol2 = Symbol('777')
const obj1= {
[symbol1]: 'value1',
[symbol2]: 'value2',
age: 19,
sex: '女'
}
// 1 for in 遍历
for (const key in obj1) {
// 注意在console看key,是不是没有遍历到symbol1
console.log(key)
}
// 2 Object.keys 遍历
Object.keys(obj1)
console.log(Object.keys(obj1))
// 3 getOwnPropertyNames
console.log(Object.getOwnPropertyNames(obj1))
// 4 JSON.stringfy
console.log(JSON.stringify(obj1))
It can be obtained by the following methods: Object.getOwnPropertySymbols(), Reflect.ownKeys()
// 1 拿到具体的symbol 属性,对象中有几个就会拿到几个
Object.getOwnPropertySymbols(obj1)
console.log(Object.getOwnPropertySymbols(obj1))
// 2 es6 的 Reflect 拿到对象的所有属性
Reflect.ownKeys(obj1)
console.log(Reflect.ownKeys(obj1))
Symbol.iterator iterator and generator for of
https://blog.csdn.net/qq1195566313/article/details/122463630?spm=1001.2014.3001.5501
14 Generics***
Generics, which define dynamic types.
14.1 Function generics
// 定义一个数字类型的函数
function num (a:number,b:number) : Array<number> {
return [a ,b];
}
num(1,2)
// 定义一个字符串类型的函数
function str (a:string,b:string) : Array<string> {
return [a ,b];
}
str('独孤','求败')
The two functions are only different in type, and the functions implemented are the same. At this time, generics can be used to optimize .
// 函数名称后面跟一个:<参数名>
// 参数名可以随便定义,这里用 T
function Add<T>(a: T, b: T): Array<T> {
return [a,b]
}
// 当使用这个函数的时候,把参数的类型传进去就可以(动态类型)
// 也是将类型写在函数名的后面 <number>
Add<number>(1,2)
Add<string>('1','2')
// 也可以简写,TS 会自动推断
Add(1,2)
Add('1','2')
You can also use different generic parameter names , as long as the number and usage can correspond.
function Sub<T,U>(a:T,b:U):Array<T|U> {
const params:Array<T|U> = [a,b]
return params
}
// 数量上和使用方式上对应
Sub<Boolean,number>(false,1)
14.2 Type aliases define generics
type A<T> = string | number | T
let a:A<boolean> = true
let a:A<boolean> = 'str'
let a:A<boolean> = 123456
let a:A<null> = null
let a:A<undefined> = undefined
14.3 Defining a generic interface
When declaring an interface, add one after the name:<参数>
interface MyInter<T> {
(arg: T): T
}
function fn<T>(arg: T): T {
return arg
}
// 使用的时候传递类型
let result: MyInter<number> = fn
result(123)
14.4 Object literal generics
let foo: {
<T>(arg: T): T
}
foo = function <T>(arg:T):T {
return arg
}
foo(123)
14.5 Generic constraints
That is, the type is followed by an extends, followed by a constraint type.
// 期望在一个泛型的变量上面,获取其 length 参数。但是,有的数据类型是没有 length 属性的。
function getLegnth<T>(arg:T) {
return arg.length
}
// 这个时候我们就可以使用 泛型约束。
Therefore, we have to constrain the generic type used, we constrain it to a type with a length attribute, here we will use interface
interface Len {
length:number
}
function getLegnth<T extends Len>(arg:T) {
return arg.length
}
getLegnth<string>('123')
14.6 Constraining objects with keyof
Which uses TS generics and generic constraints. First define the T type and use the extends keyword to inherit the subtype of the object type, then use the keyof operator to get all the keys of the T type, and its return type is a union type, and finally use the extends keyword to constrain the K type to be a keyof T union subtype of type
function prop<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
let o = {
a: 1, b: 2, c: 3 }
prop(o, 'a')
prop(o, 'd') //此时就会报错发现找不到
advanced usage
interface Data {
name: string
age: number
sex: string
}
// for in
// for(let key in obj)
type Options<T extends object> = {
//[Key in keyof T]?:T[Key]
readonly [Key in keyof T]:T[Key]
}
type B = Options<Data>
14.7 Generic classes
The declaration method is similar to the function name followed by the definition <type>
Determine the type when using new Sub()
class Sub<T>{
attr: T[] = [];
add (a:T):T[] {
return [a]
}
}
let s = new Sub<number>()
s.attr = [1,2,3]
s.add(123)
let str = new Sub<string>()
str.attr = ['1','2','3']
str.add('123')
15 tsconfig.json file
This file is tsc --init
generated with the command
Detailed configuration:
"compilerOptions": {
"incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
"tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
"diagnostics": true, // 打印诊断信息
"target": "ES5", // 目标语言的版本
"module": "CommonJS", // 生成代码的模板标准
"outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
"allowJS": true, // 允许编译器编译JS,JSX文件
"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
"outDir": "./dist", // 指定输出目录
"rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
"declarationDir": "./file", // 指定生成声明文件存放目录
"emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
"sourceMap": true, // 生成目标文件的sourceMap文件
"inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
"declarationMap": true, // 为声明文件生成sourceMap
"typeRoots": [], // 声明文件目录,默认时node_modules/@types
"types": [], // 加载的声明文件包
"removeComments":true, // 删除注释
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"noEmitOnError": true, // 发送错误时不输出任何文件
"noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
"importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
"downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
"strict": true, // 开启所有严格的类型检查
"alwaysStrict": true, // 在代码中注入'use strict'
"noImplicitAny": true, // 不允许隐式的any类型
"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
"strictFunctionTypes": true, // 不允许函数参数双向协变
"strictPropertyInitialization": true, // 类的实例属性必须初始化
"strictBindCallApply": true, // 严格的bind/call/apply检查
"noImplicitThis": true, // 不允许this有隐式的any类型
"noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
"noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
"noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
"noImplicitReturns": true, //每个分支都会有返回值
"esModuleInterop": true, // 允许export=导出,由import from 导入
"allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": {
// 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
"rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
"listEmittedFiles": true, // 打印输出文件
"listFiles": true// 打印编译的文件(包括引用的声明文件)
}
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
"src/**/*"
],
// 指定一个排除列表(include的反向操作)
"exclude": [
"demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
"files": [
"demo.ts"
]
Introduce a few commonly used ones:
1. Include
specifies that the compiled file defaults to compiling all ts files in the current directory
2.exclude
specifies the excluded files
3. target
specifies the version of compiled js such as es5 es6
4. Whether allowJS
allows compiling js files
5. Whether removeComments
deletes comments in the file during compilation
6. rootDir
directory of compiled files
7.outDir
output directory
8. sourceMap
code source file
9. strict
strict mode
10.module
default common.js optional es6 mode amd umd etc.
16 namespace namespace
Pollution caused by global variables cannot be avoided at work, and typescript provides namespace to avoid this problem.
1 Internal space, mainly used to organize code and avoid naming conflicts
2 Classes in the namespace, private by default
3 exposed through export
4 Defined by the namespace keyword
TypeScript is the same as ECMAScript 2015, any file that contains a top-level import
or export
is considered a module. Conversely, if a file does not have a top-level import
or export
declaration, its contents are considered globally visible (and thus visible to the module as well).
In the namespace, export the part you want to expose through export, otherwise you cannot read its value.
namespace a {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
namespace b {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
a.Time
b.Time
nested namespace
namespace a {
export namespace b {
export class Vue {
parameters: string
constructor(parameters: string) {
this.parameters = parameters
}
}
}
}
let v = a.b.Vue
new v('1')
Extract the namespace
// a.ts
export namespace V {
export const a = 1
}
// b.ts
import {
V} from '../observer/index'
console.log(V); //{a:1}
Simplify the namespace
namespace A {
export namespace B {
export const C = 1
}
}
import X = A.B.C
console.log(X);
merge namespace
// 重名的命名空间会合并
namespace a {
export const b = 123
}
namespace a {
export const c = 456
}
a.b
a.c
17 triple slash instruction
The triple slash reference can be understood as import, which tells the compiler the additional files that need to be introduced during the compilation process.
/// <reference path="..." />
directive is the most common type of triple-slash directive. It is used to declare the dependencies built by the file.
For example:
// a.ts
namespace A {
export const fn = () => 'a'
}
// b.ts
namespace B {
export const fn = () => 'a'
}
// index.ts 引入之后直接可以使用变量A
///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
console.log(A);
Declaration file import
For example, introducing /// into the declaration file indicates that this file uses the name declared in @types/node/index.d.ts; and, this package needs to be included together with the declaration file during the compilation phase.
Use this directive only if you need to write a d.ts file.
///<reference types="node" />
18 declaration file d.ts
statement filedeclare
When using a third-party library, we need to reference its declaration file in order to obtain the corresponding code completion, interface prompt and other functions.
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
/// <reference /> 三斜线指令
If some third-party packages do not have declaration files, we can define them ourselves.
name.d.ts Create a file to declare
For example:
// index.ts
import express from 'express'
const app = express()
const router = express.Router()
app.use('/api', router)
router.get('/list', (req, res) => {
res.json({
code: 200
})
})
app.listen(9001,()=>{
console.log(9001)
})
// express.d.ts
declare module 'express' {
interface Router {
get(path: string, cb: (req: any, res: any) => void): void
}
interface App {
use(path: string, router: any): void
listen(port: number, cb?: () => void): void
}
interface Express {
(): App
Router(): Router
}
const express: Express
export default express
}
19 Mixins
TypeScript
Mix in Mixins
In fact, vue also has mixins, which you can regard as a merger.
19.1 Object mixins
ES6's Object.assign merges multiple objects
At this point people will be inferred as a cross type Name & Age & sex
interface Name {
name: string
}
interface Age {
age: number
}
interface Sex {
sex: number
}
let people1: Name = {
name: "小满" }
let people2: Age = {
age: 20 }
let people3: Sex = {
sex: 1 }
const people = Object.assign(people1,people2,people3)
19.2 Mixins of classes
First declare two mixins classes
class A {
type: boolean = false;
changeType() {
this.type = !this.type
}
}
class B {
name: string = '张三';
getName(): string {
return this.name;
}
}
Let's create a class that combines these two mixins:
The first thing you should notice is that instead of using extends implements
, you use the class as an interface.
We can do this to achieve our goal, creating placeholder properties for the property methods that will be mixed in. This tells the compiler that these members are available at runtime. In this way, you can use the convenience brought by mixins, although you need to define some placeholder properties in advance.
class C implements A,B{
type:boolean
changeType:()=>void;
name: string;
getName:()=> string
}
Finally, create this helper function to help us do the mixin operation. It will traverse all the properties on the mixins and copy them to the target, replacing the previous placeholder properties with the real implementation code.
Object.getOwnPropertyNames() can get the properties of the object itself, remove its inherited properties, and traverse all its properties. It is an array, and traverse all its property names
Mixins(C, [A, B])
function Mixins(curCls: any, itemCls: any[]) {
itemCls.forEach(item => {
Object.getOwnPropertyNames(item.prototype).forEach(name => {
curCls.prototype[name] = item.prototype[name]
})
})
}
20 Decorator Decorator
21 webPack build TS project
22 Actual combat: TS writes publish and subscribe mode
/*
on 订阅/监听
emit 发布/注册
once 只执行一次
off 解除绑定
*/
interface EventFace {
on: (name: string, callback: Function) => void,
emit: (name: string, ...args: Array<any>) => void,
off: (name: string, fn: Function) => void,
once: (name: string, fn: Function) => void
}
interface List {
[key: string]: Array<Function>,
}
class Dispatch implements EventFace {
list: List
constructor() {
this.list = {
}
}
on(name: string, callback: Function) {
const callbackList: Array<Function> = this.list[name] || [];
callbackList.push(callback)
this.list[name] = callbackList
}
emit(name: string, ...args: Array<any>) {
let evnetName = this.list[name]
if (evnetName) {
evnetName.forEach(fn => {
fn.apply(this, args)
})
} else {
console.error('该事件未监听');
}
}
off(name: string, fn: Function) {
let evnetName = this.list[name]
if (evnetName && fn) {
let index = evnetName.findIndex(fns => fns === fn)
evnetName.splice(index, 1)
} else {
console.error('该事件未监听');
}
}
once(name: string, fn: Function) {
let decor = (...args: Array<any>) => {
fn.apply(this, args)
this.off(name, decor)
}
this.on(name, decor)
}
}
const o = new Dispatch()
o.on('abc', (...arg: Array<any>) => {
console.log(arg, 1);
})
o.once('abc', (...arg: Array<any>) => {
console.log(arg, 'once');
})
// let fn = (...arg: Array<any>) => {
// console.log(arg, 2);
// }
// o.on('abc', fn)
// o.on('ddd', (aaaa: string) => {
// console.log(aaaa);
// })
//o.off('abc', fn)
o.emit('abc', 1, true, '小满')
o.emit('abc', 2, true, '小满')
// o.emit('ddd', 'addddddddd')