文章目录
TypeScript
引言
javascript属于动态类型语言,比如:
function test(obj) {   
    console.log(obj)
}
 
obj可能只是个字符串:
test("typescript")
 
但obj也可能是个函数:
test(()=>console.log(111))
 
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)
}
 
- 限制了参数只能做 string 那些事
 
function test(msg : Function) {
  msg()
}
 
- 限制了参数只能做函数那些事
 
入门使用
1.安装typescript编译器
npm install -g typescript
 
2.编写 ts 代码
function hello(msg: string) {
  console.log(msg)
}
hello('hello,world')
 
3.执行 tsc 编译命令
tsc xxx.ts
 
编译生成 js 代码,编译后进行了类型擦除
function hello(msg) {
    console.log(msg);
}
hello('hello,world');
 
再来一个例子,用 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 })
 
编译后
function test(u) {
    console.log(u.name);
    console.log(u.age);
}
test({ name: 'zhangs', age: 18 });
 
可见,typescript 属于编译时实施类型检查(静态类型)的技术
类型
| 类型 | 例 | 备注 | 
|---|---|---|
| 字符串类型 | string | |
| 数字类型 | number | |
| 布尔类型 | boolean | |
| 数组类型 | number[],string[], boolean[] 依此类推 | |
| 任意类型 | any | 相当于又回到了没有类型的时代 | 
| 复杂类型 | type 与 interface | |
| 函数类型 | () => void | 对函数的参数和返回值进行说明 | 
| 字面量类型 | “a”|“b”|“c” | 限制变量或参数的取值 | 
| nullish类型 | null 与 undefined | |
| 泛型 | <T>,<T extends 父类型> | 
标注位置
标注变量
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
 - 这里?表示当这里的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 }
 
可以改进为
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)
 
类
基本语法
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,它还是基于原型实现的,原理参考第二章(036、037)
只读属性
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 … 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() // 错误,必须是接口中定义的方法
 
继承与接口
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()
 
最终执行的还是Son中的study的方法










