TypeScript

动态类型意味着

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

静态类型意味着

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

因此静态类型往往更规范更严谨更安全。

类型

标注位置
标注变量
 let message: string = 'hello,world'
  • 一般可以省略,因为可以根据后面的字面量推断出前面变量类型

 let message = 'hello,world'

标注参数
 function greet(name: string) {
     
 }

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

 const names = ['Alice', 'Bob', 'Eve']
 const lowercaseNames = names.map((e: string) => e.toLowerCase())
  • 可以用类型推断,推断出 e 是 string 类型

标注返回值
 function add(a: number, b: number) : number {
     return a + b
 }
  • 一般也可以省略,因为可以根据返回值做类型推断

复杂类型
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 属性

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 属性

可选属性

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

 interface Cat {
   name: string,
   age?: number
 }
 ​
 const c1: Cat = { name: '小白', age: 1 }
 const c2: Cat = { name: '小花' }                    // 正确: age 属性可选
  • 可选属性要注意处理 undefined 值

鸭子类型
 interface Cat {
   name: string
 }
 ​
 function test(cat: Cat) {
   console.log(cat.name)
 }
 ​
 const c1 = { name: '小白', age: 1 } 
 test(c1)
  • 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() }
 })
字面量类型
 function printText(s: string, alignment: "left" | "right" | "center") {
   console.log(s, alignment)
 }
 ​
 printText('hello', 'left')
 printText('hello', 'aaa') // 错误: 取值只能是 left | right | center
nullish 类型
 function test(x?: string | null) {
   console.log(x?.toUpperCase())
 }
 ​
 test('aaa')
 test(null)
 test()
  • x?: string | null 表示可能是 undefined 或者是 string 或者是 null

泛型

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

 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 }

可以改进为

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

函数定义也支持泛型

 function ref<T>(n: T): Ref<T> {
   return { value: n }
 }
 ​
 const v1 = ref("hello");    // Ref<string>
 const v2 = ref(123.3333);   // Ref<number>
 ​
 v1.value.toLocaleLowerCase()
 v2.value.toFixed(2)

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

基本语法
 class User {
     name: string;
     
     constructor(name: string) {
         this.name = name
     }
 }
 ​
 const u = new User('张三')

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

 var User = /** @class */ (function () {
     function User(name) {
         this.name = name;
     }
     return User;
 }());
 var u = new User('张三');

所以 js 中的 class,并不等价于 java 中的 class,它还是基于原型实现的

只读属性
 class User {
   readonly name: string;
   
   constructor(name: string) {
       this.name = name
   }
 }
 ​
 const u = new User('张三')
 u.name = '李四'               // 编译错误
  • readonly 是 typescript 特有的,表示该属性只读

方法
 class User {
   readonly name: string;
   
   constructor(name: string) {
       this.name = name
   }
 ​
   study() {
     console.log(`[${this.name}]正在学习`)
   }
 }
 ​
 const u = new User('张三')
 u.study()
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)
  • 注意,需要在编译时加上 tsc --target es6 .\xxx.ts 选项

  • es6 等价于 es2015,再此之上还有 es2016 ... es2022

类与接口
 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() // 错误,必须是接口中定义的方法
继承与接口
 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()
  • 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()

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐