• TypeScript学习笔记


    TypeScript

    引言

    javascript属于动态类型语言,比如:

    function test(obj) {   
        console.log(obj)
    }
    
    • 1
    • 2
    • 3

    obj可能只是个字符串

    test("typescript")
    
    • 1

    但obj也可能是个函数:

    test(()=>console.log(111))
    
    • 1

    obj 类型不确定,就给后期使用者带来了麻烦,一旦参数传不对,代码就崩溃了

    动态类型意味着

    • 运行代码时才知道发生什么 (running the code to see what happens)

    静态类型意味着

    • 在代码运行前,就对它的行为做出预测 (make predications about what code is expected before it runs)

    下面的 typescript 代码,就在代码运行前对参数加入了约束限制

    function test(msg : string) {
    	console.log(obj)
    }
    
    • 1
    • 2
    • 3
    • 限制了参数只能做 string 那些事
    function test(msg : Function) {
      msg()
    }
    
    • 1
    • 2
    • 3
    • 限制了参数只能做函数那些事

    入门使用

    1.安装typescript编译器

    npm install -g typescript
    
    • 1

    2.编写 ts 代码

    function hello(msg: string) {
      console.log(msg)
    }
    
    hello('hello,world')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.执行 tsc 编译命令

    tsc xxx.ts
    
    • 1

    编译生成 js 代码,编译后进行了类型擦除

    function hello(msg) {
        console.log(msg);
    }
    hello('hello,world');
    
    • 1
    • 2
    • 3
    • 4

    再来一个例子,用 interface 定义用户类型

    interface User {
      name: string,
      age: number
    }
    
    function test(u: User): void {
      console.log(u.name)
      console.log(u.age)
    }
    
    test({ name: 'zhangs', age: 18 })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    编译后

    function test(u) {
        console.log(u.name);
        console.log(u.age);
    }
    test({ name: 'zhangs', age: 18 });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可见,typescript 属于编译时实施类型检查(静态类型)的技术

    类型

    类型备注
    字符串类型string
    数字类型number
    布尔类型boolean
    数组类型number[],string[], boolean[] 依此类推
    任意类型any相当于又回到了没有类型的时代
    复杂类型type 与 interface
    函数类型() => void对函数的参数和返回值进行说明
    字面量类型“a”|“b”|“c”限制变量或参数的取值
    nullish类型null 与 undefined
    泛型
    标注位置
    标注变量
    let message: string = 'hello,world'
    
    • 1
    • 一般可以省略,因为可以根据后面的字面量推断出前面变量类型
    let message = 'hello,world'
    
    • 1
    标注参数
    function greet(name: string) {
    }
    
    • 1
    • 2

    很多时候,都能够推断出参数类型

    const names = ['Alice', 'Bob', 'Eve']
    const lowercaseNames = names.map((e: string) => e.toLowerCase())
    
    • 1
    • 2
    • 可以用类型推断,推断出 e 是 string 类型
    标注返回值
    function add(a: number, b: number) : number {
        return a + b
    }
    
    • 1
    • 2
    • 3
    • 一般也可以省略,因为可以根据返回值做类型推断
    复杂类型
    type
    type Cat = {
      name: string,
      age: number
    }
    
    const c1: Cat = { name: '小白', age: 1 }
    const c2: Cat = { name: '小花' }					  // 错误: 缺少 age 属性
    const c3: Cat = { name: '小黑', age: 1, sex: '公' } // 错误: 多出 sex 属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    interface
    interface Cat {
      name: string,
      age: number
    }
    
    const c1: Cat = { name: '小白', age: 1 }
    const c2: Cat = { name: '小花' }					  // 错误: 缺少 age 属性
    const c3: Cat = { name: '小黑', age: 1, sex: '公' } // 错误: 多出 sex 属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    可选属性

    如果需要某个属性可选,可以用下面的语法

    interface Cat {
      name: string,
      age?: number
    }
    
    const c1: Cat = { name: '小白', age: 1 }
    const c2: Cat = { name: '小花' }					  // 正确: age 属性可选
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 可选属性要注意处理 undefined 值
    鸭子类型
    interface Cat {
      name: string
    }
    
    function test(cat: Cat) {
      console.log(cat.name)
    }
    
    const c1 = { name: '小白', age: 1 } 
    test(c1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • const c1 并没有声明类型为 Cat,但它与 Cat 类型有一样的属性(比较相似),也可以被当作是 Cat 类型
    方法类型
    interface Api {
      foo(): void,
      bar(str: string): string
    }
    
    function test(api: Api) {
      api.foo()
      console.log(api.bar('hello'))
    }
    
    test({
      foo() { console.log('ok') },
      bar(str: string) { return str.toUpperCase() }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    字面量类型
    function printText(s: string, alignment: "left" | "right" | "center") {
      console.log(s, alignment)
    }
    
    printText('hello', 'left')
    printText('hello', 'aaa') // 错误: 取值只能是 left | right | center
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    nullish 类型
    function test(x?: string | null) {
      console.log(x?.toUpperCase())
    }
    
    test('aaa')
    test(null)
    test()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • x?: string | null 表示可能是 undefined 或者是 string 或者是 null
    • 这里?表示当这里的x不null或undefined就调用toUpperCase()方法
    泛型

    下面的几个类型声明显然有一定的相似性

    interface RefString {
      value: string
    }
    
    interface RefNumber {
      value: number
    }
    
    interface RefBoolean {
      value: boolean
    }
    
    const r1: RefString = { value: 'hello' }
    const r2: RefNumber = { value: 123 }
    const r3: RefBoolean = { value: true }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以改进为

    interface Ref<T> {
      value: T
    }
    
    const r1: Ref<string> = { value: 'hello' }
    const r2: Ref<number> = { value: 123 }
    const r3: Ref<boolean> = { value: true }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 泛型的要点就是 <类型参数>,把【类型】也当作一个变化的要素,像参数一样传递过来,这样就可以派生出结构相似的新类型

    函数定义也支持泛型

    function ref<T>(n: T): Ref<T> {
      return { value: n }
    }
    
    const v1 = ref("hello"); 	// Ref
    const v2 = ref(123.3333);	// Ref
    
    v1.value.toLocaleLowerCase()
    v2.value.toFixed(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    关于 TypeScript 与 JavaScript 中的类语法不是重点,class 相关语法只是起到辅助作用,更重要的是前面讲的 interface

    基本语法

    class User {
        name: string;
        
        constructor(name: string) {
            this.name = name
        }
    }
    
    const u = new User('张三')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其实会被编译成这个样子(默认 --target=es3)

    var User = /** @class */ (function () {
        function User(name) {
            this.name = name;
        }
        return User;
    }());
    var u = new User('张三');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    所以 js 中的 class,并不等价于 java 中的 class,它还是基于原型实现的,原理参考第二章(036、037)

    只读属性

    class User {
      readonly name: string;
      
      constructor(name: string) {
          this.name = name
      }
    }
    
    const u = new User('张三')
    u.name = '李四'				// 编译错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • readonly 是 typescript 特有的,表示该属性只读

    方法

    class User {
      readonly name: string;
      
      constructor(name: string) {
          this.name = name
      }
    
      study() {
        console.log(`[${this.name}]正在学习`)
      }
    }
    
    const u = new User('张三')
    u.study()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    get,set

    class User {
      _name: string;
    
      constructor(name: string) {
        this._name = name
      }
    
      get name() {
        return this._name
      }
    
      set name(name: string) {
        this._name = name
      }
    }
    
    const u = new User('张三')
    console.log(u.name)
    u.name = '李四'
    console.log(u.name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 注意,需要在编译时加上 tsc --target es6 .\xxx.ts 选项
    • es6 等价于 es2015,再此之上还有 es2016 … es2023

    类与接口

    interface User {
      name: string
      study(course: string): void
    }
    
    class UserImpl implements User {
      name: string;
      constructor(name: string) {
        this.name = name
      }
      study(course: string) {
        console.log(`[${this.name}]正在学习[${course}]`)
      }
      foo() { }
    }
    
    const user: User = new UserImpl('张三')
    user.study('Typescript')
    user.foo() // 错误,必须是接口中定义的方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    继承与接口

    interface Flyable {
      fly(): void
    }
    
    class Animal {
      name: string;
      constructor(name: string) {
        this.name = name
      }
    }
    
    class Bird extends Animal implements Flyable {
      fly() {
        console.log(`${this.name}在飞翔`)
      }
    }
    
    const b: Flyable & Animal = new Bird("小花")
    b.fly()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • Flyable & Animal 表示变量是 flyable 类型,同时也是 Animal 类型

    方法重写

    class Father {
      study(): void {
        console.log(`father study`)
      }
    }
    
    class Son extends Father {  
      study(): void {
        super.study()
        console.log(`son study`)
      }
    }
    
    const f: Father = new Son()
    f.study()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    最终执行的还是Son中的study的方法

  • 相关阅读:
    快速排序模拟实现
    .net----委托和事件
    NVIDIA CUDA 高度并行处理器编程(八):并行模式:直方图计算
    Selenium+dddocr轻松解决Web自动化验证码识别
    【LeetCode刷题记录】92. 反转链表 II & 25. K 个一组翻转链表
    package.json scripts 脚本的作用
    五个DIY表情背后的故事
    Kafka - 3.x Kafka命令行操作
    setInterval、setTimeout和requestAnimationFrame
    USB 协议 (四) USB HOST 侧 的概念详解
  • 原文地址:https://blog.csdn.net/m0_63837020/article/details/136173645