运行#
JavaScript 的超集,面向对象、类型检测等
class Greeting {
constructor(public message: string) { }
greet() {
console.log(this.message);
}
}
let g = new Greeting("Hello, world!");
g.greet(); // Output: "Hello, world!"ts命令
tsc hello.ts # 编译成js文件
node hello.js bash在node中运行
ts-node hello.tsbash类型#
| 数据类型 | 关键字 | 描述 | 示例 |
|---|---|---|---|
| 布尔值 | boolean | true/false | let isDone: boolean = true |
| 数字 | number | 整数或双精度64位浮点数 | let decimal: number = 6; |
| 字符串 | string | 文本 | let greeting: string = "Hello, world!"; |
| 任意值 | any | 可以赋予任意类型 | let variable: any = "Hello, world!"; |
| 空值 | void | 没有返回值的函数 | |
| Never | never | 会抛出异常或死循环的函数返回值 | |
| Null | null | 空对象引用 | |
| Undefined | undefined | 未定义的值 | |
| 复杂类型 | |||
| 数组 | Array | 有序的集合,不限制长度、元素类型 | let numbers: number[] = [1, 2, 3]; |
| 对象 | Object | 任意属性的集合 | |
| 函数 | Function | 接受参数并返回值 | |
| 元组 | Tuple | 相比数组,有固定对应位置类型和长度 | let tuple: [string, number] = ["hello", 123]; |
| 枚举 | enum | 定义数值集合 |
类型别名#
用 type 关键字,自定义一个(复杂)类型,方便重复使用。
type Person = {
name: string;
age: number;
}
let Alex: Person = {
name: "Alex",
age: 15
}
let Bob: Person = {
name: "Bob",
age: 20
}ts枚举类型#
字面量类型:右值,表示一组固定的值
const str2: 'Hello TS' = 'Hello TS'更高效(无需编译转换)
关键字 enum定义数值集合,可以用数字或字符串作为值
- 原理:将枚举类型编译成对象,对象属性名为枚举值,属性值为对应值
enum Direction { Up, Down, Left, Right } // 定义枚举类型
function changeDirection(direction: Direction) {} // 参数为枚举类型
changeDirection(Direction.Left) // callts编译后的代码:
var Direction;
(function (Direction) { // 自调用函数
Direction["Up"] = "UP";
Direction["Down"] = "DOWN";
Direction["Left"] = "LEFT";
Direction["Right"] = "RIGHT";
})(Direction || (Direction = {})); // Direction: { Up: 'UP', Down: 'DOWN', Left: 'LEFT', Right: 'RIGHT' }
function changeDirection(direction) {
console.log("方向:" + direction);
}
changeDirection(Direction.Up); // 方向:UPjs联合类型#
通过管道 | 将变量设置多种类型,其中任意一种。
let arr: (string|number|boolean)[] = ["hello", 123, true]
arr.push("world"); // OK
arr.push(null); // Errorts交叉类型#
通过 & 将变量设置多种类型,所有类型都必须满足。
interface Person {
name: string;
age: number;
}
function greetPerson(person: Person & { gender: string }) {
console.log(`Hello, ${person.name}! You are ${person.age} years old. You are a ${person.gender}.`);
}
let john: Person & { gender: string } = {
name: "John",
age: 30,
gender: "male"
};
greetPerson(john);ts同名属性类型冲突处理:视为联合类型
类型断言#
若自动推断出来的类型是 number|undefined,可以自己断言明确变量类型
将一个变量或表达式转换为另一种类型。
let someValue: any = "hello";
let strLength: number = (someValue as string).length;
// 转换 HTMLElement | null,获得href属性
const aLink = document.getElementById('link') as HTMLAnchorElement
consl.log(aLink.href)ts类型保护#
通过 typeof 关键字,判断变量是否属于某种类型。
在类型上下位中,引用变量的类型
function isNumber(value: any): value is number {
return typeof value === "number";
}
function add(x: number, y: number): number {
if (isNumber(x) && isNumber(y)) {
return x + y;
} else {
throw new Error("Both arguments must be numbers.");
}
}ts类型兼容性#
一种类型的值是否可以被视为另一种类型的值?
TS 属于Structural Type System(结构化类型系统): 结构(属性和方法)相同的类型之间兼容
对象/接口:X是Y的子集 -> X兼容Y
class Point2D { x: number; y: number }
class Point3D extends Point2D { z: number }
const p1: Point2D = new Point3D() // 成员多的可以赋值给少的
interface P2D { x: number; y: number }
let p2: P2D = new Point3D() // 接口同理ts函数:类型相同
- X参数比Y多 -> X兼容Y
- 返回值:同 对象的兼容性
type F1 = (a: number) => void
type F2 = (a: number, b: number) => void
type F3 = () => { name: string }
type F4 = () => { name: string; age: number }
let f1: F1
let f2: F2 = f1
let f7: F7 = f8: F8ts函数#
参数 + 返回值 的类型
function add(x: number, y: number): number {
return x + y;
}
const myAdd = (x: number, y: number): number => x+y;ts可选参数#
参数名后添加 ?,表示参数可选。
function greet(name: string, age?: number) {
if (age) {
console.log(`Hello, ${name}! You are ${age} years old.`);
} else {
console.log(`Hello, ${name}!`);
}
}
// 默认参数
function greet2(name: string, age: number = 18) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
greet("Alex", 30); // Hello, Alex! You are 30 years old.
greet("Ben"); // Hello, Ben!
greet2("Chloe"); // Hello, Chloe! You are 18 years old.ts剩余参数#
function sum(...numbers: number[]) {
let result = 0;
for (let i = 0; i < numbers.length; i++) {
result += numbers[i];
}
return result;
}
console.log(sum(1, 2, 3)); // Output: 6ts对象#
属性 + 方法 的类型
- 一行内写多个:加
;分隔 - 方法可用箭头函数形式
let person: { name: string; age: number; sayHi: () => void; greet(name: string): void } = {
name: 'Alex',
age: 18,
sayHi() {},
greet(name) {}
}
// 可选属性
function myAxios(config: { url: string; method?: string }) {}ts接口#
为了给对象指定类型,实现复用。
interface IPerson { // 定义接口
name: string
age: number
sayHi(): void
}
let person: IPerson = {
name: 'Alex',
age: 18,
sayHi() {}
}ts接口和类型别名对比
- 接口仅为对象指定类型
- 类型别名可以给任意(复杂)类型起别名
type IPerson = { // 定义类型别名
name: string
age: number
sayHi(): void
}ts抽象类和接口的区别
- 继承:一个类只能继承一个抽象类,可以实现多个接口。
- 抽象类可以包含一些具体的方法(有实现),可以有构造函数和成员变量。接口(Interface)仅定义结构,不能包含任何实现。接口的所有属性和方法默认为抽象的,不能有构造函数。
- 接口不能使用访问修饰符,所有属性和方法默认是 public
类#
不仅是class, 还作为一种类型存在
public默认,外部可访问private私有,外部不可访问protected受保护,外部不可访问,子类可访问readonly只读属性,仅在构造函数中初始化赋值- 需供明确的类型
- 接口中也能用
static静态属性,类中所有实例共享abstract抽象类,不能实例化,子类必须实现抽象方法
声明成员方法: sayHi (params) {xxx} 不用关键字
存取器: get 和 set 方法,用于访问和修改属性
class Person {
name: string // 默认不加的为public
private age: number //私有成员,外部无法访问
protected gender: boolean // 外部无法访问,子类可以访问
readonly rid: number // 只能在构造函数中初始化
constructor(name: string, age: number, rid: number) {
this.name = name
this.age = age
this.gender = true
this.rid = rid
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
class Student extends Person {
identity: string = 'student'
constructor(name: string, age: number) {
super(name, age)
console.log(this.gender)
}
}
let tom = new Student('tom', 15, 123)
console.log(tom) // { name: 'tom', age: 15, gender: true, identity:'student', rid: 123 }
tom.sayHi('hello') // Output: I am tom, hellotsclass Animal {
static num = 0
constructor(public name: string) {
Animal.num++
}
eat() {
console.log(`${this.name} is eating`)
}
abstract sleep(): void
}
class Dog extends Animal {
sleep() {
console.log(`${this.name} is sleeping`)
}
}
let dog = new Dog('wangcai')
dog.eat() // Output: wangcai is eating
dog.sleep() // Output: wangcai is sleeping
console.log(Animal.num) // Output: 1ts继承与实现#
extends继承父类implements实现接口
interface Singale {
sing(): void
name: string
}
class Prompt implements Singale {
name = 'CNM'
sing() {
console.log('阿米诺斯')
}
}ts方法拓展#
Object.assign()合并对象Object.create()创建对象Object.getOwnPropertyDescriptor()获取属性描述符Object.getOwnPropertyNames()获取所有属性名Object.getOwnPropertySymbols()获取所有 Symbol 属性名Object.getPrototypeOf()获取原型对象Object.setPrototypeOf()设置原型对象Object.is()判断两个值是否相等Object.keys()获取对象所有可枚举属性名Object.values()获取对象所有可枚举属性值Object.entries()获取对象所有可枚举属性名和值Object.fromEntries()从键值对数组创建对象
interface Person {
name: string
age: number
}
interface Student extends Person {
grade: number
}
let person: Person = {
name: 'Alex',
age: 18
}
let student: Student = Object.assign(person, { grade: 3 })
console.log(student) // Output: { name: 'Alex', age: 18, grade: 3 }
let obj = Object.create(null)
obj.name = 'Alex'
console.log(obj) // Output: { name: 'Alex' }
let desc = Object.getOwnPropertyDescriptor(person, 'name')
console.log(desc) // Output: { value: 'Alex', writable: true, enumerable: true, configurable: true }
let names = Object.getOwnPropertyNames(person)
console.log(names) // Output: [ 'name', 'age' ]
let symbols = Object.getOwnPropertySymbols(person)
console.log(symbols) // Output: []
let proto = Object.getPrototypeOf(person)
console.log(proto) // Output: {}
let obj2 = { name: 'Alex', age: 18 }
Object.setPrototypeOf(student, obj2)
console.log(Object.getPrototypeOf(student)) // Output: { name: 'Alex', age: 18 }
console.log(Object.is(1, 1)) // Output: true
console.log(Object.is(1, '1')) // Output: false
let obj3 = { name: 'Alex', age: 18, gender: 'female' }
let keys = Object.keys(obj3)
console.log(keys) // Output: [ 'name', 'age', 'gender' ]
let values = Object.values(obj3)
console.log(values) // Output: [ 'Alex', 18, 'female' ]
let entries = Object.entries(obj3)
console.log(entries) // Output: [ [ 'name', 'Alex' ], [ 'age', 18 ], [ 'gender', 'female' ] ]
let obj4 = Object.fromEntries([['name', 'Alex'], ['age', 18]])
console.log(obj4) // Output: { name: 'Alex', age: 18 }
type Excluded = 'name' | 'age'
type OmitPerson = Omit<Person, Excluded>
let person2: OmitPerson = {
gender: 'female'
}
console.log(person2) // Output: { gender: 'female' }
type Extracted = 'name' | 'age'
type ExtractPerson = Extract<Person, Extracted>
let person3: ExtractPerson = {
name: 'Alex',
age: 18
}
console.log(person3) // Output: { name: 'Alex', age: 18 }
type NonNullablePerson = NonNullable<Person>
let person4: NonNullablePerson = {
name: 'Alex',
age: 18,
gender: null
}
console.log(person4) // Output: { name: 'Alex', age: 18 }ts泛型#
允许函数、类、接口、类型别名,实现复用,在定义时使用类型参数。
<T> 定义一个类型变量,调用时指定类型(可自动推断)。
function identity<T>(arg: T): T {
return arg
}
let output = identity<string>('hello');
console.log(output) // Output: hello
class Queue<T> {
private data: T[] = []
push(item: T) {
this.data.push(item)
}
pop(): T | undefined {
return this.data.shift()
}
}
let queue = new Queue<number>()
queue.push(1)
queue.push(2)
queue.push(3)
console.log(queue.pop()) // Output: 1
console.log(queue.pop()) // Output: 2
console.log(queue.pop()) // Output: 3
console.log(queue.pop()) // Output: undefinedts约束#
- 创建具有
length属性的约束接口ILength - 函数的参数类型使用
extends关键字添加约束,要求返回类型T必须具有相应属性 - 第二个类型变量
Key,受T约束,即只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性
keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
interface ILength { length: number }
function getLength<T extends ILength>(arg: T): T {
console.log(arg.length)
return arg
}
console.log(getLength('hello')) // Output: 5
console.log(getLength([0, 1, 2])) // Output: 3
console.log(getLength({ length: 4, name: 'Alex' })) // Output: 4
console.log(getLength(123)) // Error: Type 'number' is not assignable to type 'ILength'.
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key]
}
getProp({ name: 'jack', age: 18 }, 'age') // 18
getProp(18, 'toFixed') // [Function: toFixed]
getProp('abc', 1) // b
console.log('object'[1]) // Output: bts泛型接口#
- 添加类型变量
<T>,接口中所有其他成员可以使用T - 使用时需要显式指定
interface IArray<T> {
push(item: T): void
pop(): T | undefined
}
class Array<T> implements IArray<T> {
private data: T[] = []
push(item: T) {
this.data.push(item)
}
pop(): T | undefined {
return this.data.shift()
}
}
let arr = new Array<number>()
arr.push(1)
arr.push(2)
arr.push(3)
console.log(arr.pop()) // Output: 1
console.log(arr.pop()) // Output: 2
console.log(arr.pop()) // Output: 3
console.log(arr.pop()) // Output: undefinedts泛型类#
class Box<T> {
private data: T
constructor(data: T) {
this.data = data
}
getData(): T {
return this.data
}
}
let box = new Box<string>('hello')
console.log(box.getData()) // Output: hellots工具类型#
Partial<T>所有属性可选Readonly<T>所有属性只读Required<T>所有属性必填Pick<T, K>选择属性KOmit<T, K>排除属性KRecord<K, T>创建一个对象,其键为K,值为TExclude<T, U>从T中排除UExtract<T, U>提取T中存在于U的类型NonNullable<T>排除null和undefinedParameters<T>获取函数参数类型ConstructorParameters<T>获取构造函数参数类型ReturnType<T>获取函数返回值类型InstanceType<T>获取构造函数返回值类型ThisType<T>用于this类型推断
type Partial<T> = {
[P in keyof T]?: T[P]
}
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Required<T> = {
[P in keyof T]-?: T[P]
}
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Record<K extends keyof any, T> = {
[P in K]: T
}
type Exclude<T, U> = T extends U? never : T
type Extract<T, U> = T extends U ? T : never
type NonNullable<T> = T extends null | undefined ? never : T
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : never
type ThisType<T> = T extends infer R ? ThisType<R> : Tts集合#
es6新增
- 数据类型:Symbol
- 两种数据结构:Set、Map
symbol是为了标识唯一性