0%

ES6 模块化

export

ES6 模块化 是 静态编译,在编译阶段就已经引入,而非执行阶段引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export const name = 'tang'
const age = 18
const foo = function () {
console.log('function foo');
}
class A {}

const obj = {
type: 'array',
data: []
}

export {
age,
foo as Foo1 ,
A as ClassA,
obj
}
  1. export 导出的是一个接口而非一个具体的值

    1
    2
    3
    export 'string'               // error
    const a = 'a'
    export a // error
  2. export 导出的内容与其对应的值是 动态绑定的,导出的内容发生变化,引入内容值同时变化

  3. export 命令可以出现在模块中的顶层的任何位置,不是文件顶部,而是不能出现在块级作用域内

阅读全文 »

unknown 类型

ts 3.0 顶级类型

  1. 任何类型都可以赋值给 unknown
  2. 如果没有 类型断言基于控制流的类型细化时
    unknown 只能赋值给 unknown | any,不能赋值给其他类型
  3. 如果没有 类型断言基于控制流的类型细化时
    不能在它上面进行任何操作
  4. unknown 与其他类型组成的交叉类型,最后都等于其他类型
  5. unknown 与其他类型(除 any)组成的联合类型,都等于 unknown 类型
  6. never 类型是 unknown 的子类型
  7. keyof unknown 等于类型 never
  8. 只能对 unknown 进行 等或不等操作,不能进行其他操作
  9. unknown 类型的值不能访问其属性、不能作为函数调用和作为类创建实例
  10. 使用 映射类型 时,如果遍历的是 unknown 类型,则不会映射任何属性
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
// 1. 
let value1: unknown
value1 = 100
value1 = 'aaa'
value1 = false

// 2.
let value2: unknown
// let value3: string = value2 // error 不能将类型“unknown”分配给类型“string”
value2 = value1 // ok

// 3.
let value4: unknown
// value4 += 1 // error 不能操作(自加)

// 4.
type type1 = string & unknown // string
type type2 = number & unknown // number
type type3 = unknown & unknown // unknown
type type4 = unknown & string[] // string[]

// 5.
type type5 = string | unknown // unknown
type type6 = any | unknown // any
type type7 = number[] | unknown // any


// 6.
// 条件类型:如三元运算符
type type8 = never extends unknown ? true : false // any

// 7.
type type9 = keyof unknown // never

// 8.
console.log(value1 !== value2); // false
// value1 =+ value2 // error

// 9.
let value5: unknown
// value5.age // error
// value() // error

// 10.
type TType<T> = {
[P in keyof T]: number
}
type type10 = TType<any> // [x: string]: number;
type type11 = TType<unknown> // 没有属性

条件类型

阅读全文 »

映射类型

从旧类型中创建新类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface InfoType {
age: number,
name: string,
sex: string
}
interface ReadonlyType {
readonly age: number
}

// 类型字段多,重定义不方便
// 映射类型
type ReadonlyMapType<T> = {
// in ==> for .. in
readonly [P in keyof T]: T[P]
}
type ReadOnlyInfo = ReadonlyMapType<InfoType>
let _info: ReadOnlyInfo = {
age: 18,
name: 'tan',
sex: 'man'
}
// _info.age = 100 // error: readonly

Readonly && Partical

  • ts 内置了 常用的两种
  • 把类型转换为 每一个都是只读的每一个都是可选的
阅读全文 »

this 类型

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
class Counter {
constructor (public count: number = 0) {}
public add (value: number) {
this.count += value
return this
}
public substract (value: number) {
this.count -= value
return this
}
}

let c = new Counter(10)
console.log(c.add(10).substract(2)); // Counter {count: 18}
// return this 链式调用

class PowCounter extends Counter {
constructor (public count: number = 0) {
super(count)
}
public pow(value: number) {
this.count = this.count ** value
return this
}
}
let pc = new PowCounter(5)
console.log(pc.add(1).substract(2).pow(3)); // PowCounter {count: 64}

索引类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface IInfo {
name: string,
age: number,
}

let info: keyof IInfo // 相当于 字符串字面量类型 'name' | 'age' 的联合类型
info = 'name'
info = 'age'
info = 'n' // error

// K 是由 T类型的值的 key 组成的数组
function getValue <T, K extends keyof T>(obj: T, names: K[]): T[K][] {
return names.map(item => obj[item])
}
const infoObj = {
name: 'tan',
age: 18
}
let values: (string | number)[] = getValue(infoObj, ['name', 'age'])
console.log(values); // ["tan", 18]

索引访问操作符

阅读全文 »

交叉类型

多个类型合并为一个类型,包含所需的 所有类型 的特性

1
2
3
4
5
6
// T & U 交叉类型
const merge = <T, U>(arg1: T, arg2: U): T & U => {
let res = {} as T & U
res = Object.assign(arg1, arg2)
return res
}

联合类型

联合类型表示一个值可以是几种类型之一

阅读全文 »

resolve

1
2
3
4
5
6
7
8
9
10
11
// resolve 配置 webpack 如何寻找模块所对应的文件
resolve: {
// 在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试访问文件是否存在
extensions: [],
// 通过别名来把原导入路径映射成一个新的导入路径
alias: {
comonents: './src/components/'
},
// modules 配置 Webpack 去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录下寻找
modules: ['./src/components','node_modules']
}

modules

有时项目当中会有一些模块被大量 被其他模块依赖和导入,由于其他模块的位置分布不定,针对不同的文件都要去计算被导入文件的相对路径,
这个路径有时候会很长,如:import Button from '../../../components/button'
这时可以通过 webpack 进行优化,假如那些被大量导入的模块都在 ./src/components/ 目录下,通过 webpack 配置

1
2
3
4
resolve: {
// modules 配置 Webpack 去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录下寻找
modules: ['./src/components','node_modules']
}
阅读全文 »

ES5 实现 class

  1. 构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function ClassPerson (name, age) {
    'use strict';
    var name = arguments.length && arguments[0] !== undefined
    ? arguments[0]
    : name
    var age = arguments.length && arguments[1] !== undefined
    ? arguments[1]
    : age

    // 是否通过 new 创建
    classCheck(this, ClassPerson)
    this.name = name
    this.age = age
    }
    // 检测 是否 new 实例
    function classCheck (instance, constructor) {
    if (!(instance instanceof constructor)) throw new Error('should exec with new keyword')
    }
  2. 属性方法

    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
    var handleProps = (function () {
    function defineProps (target, props) {
    for (var i = 0; i < props.length; i++) {
    var descriptor = props[i]
    descriptor.enumerable = descriptor.enumerable || false
    descriptor.configurable = descriptor.configurable || true
    Object.defineProperty(target, descriptor.key, descriptor)
    }
    }

    return function (constructor, protoProps, staticProps) {
    if (protoProps) defineProps(constructor.prototype, protoProps)
    if (staticProps) defineProps(constructor, staticProps)
    }
    })()

    // 普通方法 和 static 方法
    // 可用 babel 转换 ?
    handleProps(ClassPerson, [
    {
    key: 'getName',
    value: function () {
    console.log(this.name);
    }
    }
    ], [
    {
    key: 'getClassName',
    value: function () {
    console.log(this.name);
    }
    }
    ])

  • new.target 用于检测 方法或构造函数 是否是通过 new 被调用的
  • 通过 new 初始化的 构造函数中,new.target 返回一个指向构造函数的引用
  • 普通函数 则 返回 undefined
1
2
3
4
5
6
7
8
9
10
11
12
class Point {
constructor () {
console.log(new.target); // [class Point]
}
}
const p = new Point()
// console
// class Point {
// constructor () {
// console.log(new.target);
// }
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Parent {
constructor () {
console.log(new.target) // [class Child extends Parent]
if (new.target === Parent) throw new Error('不能实例化')
}
}

class Child extends Parent {
constructor () {
// 继承 构造函数 必须 super
super()
}
}

const p = new Parent() // Error: 不能实例化
const c = new Child() // 只能通过 子类创建实例
// console
// class Child extends Parent {
// constructor () {
// super()
// }
// }
  • 检测 是否通过 new 创建 的实例
    1
    2
    3
    function classCheck (instance, constructor) {
    if (!(instance instanceof constructor)) throw new Error('should exec with new keyword')
    }

ES6 Class 语法糖,任然是通过 构造函数 创建实例的

ES5 创建一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
// ES5 构造函数创建
function Point (x, y) {
this.x = x
this.y = y
}

Point.prototype.getPosition = function () {
return '(' + this.x + ', ' + this.y + ')'
}

var p1 = new Point(10, 20)
console.log(p1) // Point { x: 10, y: 20 }
console.log(p1.getPosition()) // (10, 20)

ES6 Class

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
// ES6 class 定义 类
class Point {
z = 0 // 可以直接声明构造函数属性,

// 构造函数 不写为空 constructor() {}
constructor (x, y, z) {
this.x = x
this.y = y
this.z = z

// 当自定义返回值,则 对象 p1 不再是 构造函数的实例
// return { a: 'a' }
}
getPosition () {
return `(${this.x}, ${this.y})`
}
// 静态方法
static getClassName () {
return Point.name // 每一个 class 都有一个 name 属性,如同 每个函数都有 name
}
}

const p1 = new Point(1, 23)
console.log(p1) // Point { z: 3, x: 1, y: 2 }
console.log(p1.getPosition()) // (1, 2)

// console.log(p1 instanceof Point) // false

console.log(p1.hasOwnProperty('x')) // true 构造函数上
console.log(p1.hasOwnProperty('getPosition')) // false
console.log(p1.__proto__.hasOwnProperty('getPosition')) // true 实际 在 原型上

// console.log(p1.getClassName()) // p1.getClassName is not a function 实例无法访问静态属性
console.log(Point.getClassName()) // Point class 本身才可以访问
阅读全文 »

  • 在TypeScript中,我们使用接口(Interfaces)来定义对象的类型。除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述
  • TypeScript 的核心原则之一是对值所具有的结构进行类型检查, 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
  • interface 这个概念在js中并没有,所以 interface 编译后并不会呈现到 js 中,只会进行静态的类型检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface NameInfo {
firstName: string,
lastName: string
}

// const getFullName = ({ firstName, lastName }: { firstName: string, lastName: string }): string => {
const getFullName = ({ firstName, lastName }: NameInfo): string => {
return `firstName: ${firstName}, lastName: ${lastName}`
}

const obj = {
firstName: 'tan',
lastName: 'ln'
}
getFullName(obj)
  1. 可选属性

    1
    2
    3
    4
    5
    interface Person {
    name: string,
    age: number,
    car?: string // 可选
    }
  2. 索引签名 任意属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    interface NameInfo {
    firstName: string,
    lastName: string,
    [prop: string]: any // 索引签名 多余属性检测
    }
    getFullName({
    firstName: 'tan',
    lastName: 'ln',
    age: 0
    })
  3. 只读属性

    1
    2
    3
    4
    interface Person {
    car?: string, // 可选
    readonly type: string // 只读
    }

数组接口

1
2
3
4
5
6
interface ArrInterface {
0: number,
readonly 1: string
}
let arr: ArrInterface = [0, '0']
arr[1] = '' // error
阅读全文 »