Appearance
TypeScript 与 JavaScript 主要特性区别详解
目录
类型系统
静态类型 vs 动态类型
JavaScript(动态类型)
javascript
let name = '张三'
name = 123 // ✅ 运行时可以改变类型,不报错
name = true // ✅ 任意类型都可以赋值
function add(a, b) {
return a + b
}
add(1, 2) // 3
add('1', '2') // "12" - 字符串拼接,可能不是预期结果
add(1, '2') // "12" - 混合类型运算TypeScript(静态类型)
typescript
let name: string = '张三'
name = 123 // ❌ 编译错误:不能将类型"number"分配给类型"string"
function add(a: number, b: number): number {
return a + b
}
add(1, 2) // ✅ 正确
add('1', '2') // ❌ 编译错误:参数类型不匹配基础类型对比
| 类型 | JavaScript | TypeScript |
|---|---|---|
| 布尔值 | true/false | boolean |
| 数字 | 1, 1.5, 0xff | number |
| 字符串 | "hello" | string |
| 数组 | [1, 2, 3] | number[] 或 Array<number> |
| 元组 | 无 | [string, number] |
| 枚举 | 无 | enum Color {Red, Green} |
| 任意类型 | 所有值 | any |
| 空值 | null/undefined | void, null, undefined |
| 永不返回 | 无 | never |
| 未知类型 | 无 | unknown |
TypeScript 特有类型
typescript
// 元组 - 固定长度和类型的数组
let tuple: [string, number, boolean]
tuple = ['hello', 42, true] // ✅
tuple = ['hello', 42] // ❌ 长度不匹配
tuple = [42, 'hello', true] // ❌ 类型顺序不匹配
// 枚举
enum Direction {
Up = 1,
Down, // 2(自动递增)
Left, // 3
Right // 4
}
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
// 字符串枚举
enum UserRole {
Admin = 'ADMIN',
User = 'USER',
Guest = 'GUEST'
}
// unknown - 比 any 更安全的任意类型
let notSure: unknown = 4
notSure = 'maybe a string'
notSure = false
if (typeof notSure === 'string') {
console.log(notSure.toUpperCase()) // ✅ 类型收窄后可用
}
// never - 永不存在的值
function error(message: string): never {
throw new Error(message)
}
function infiniteLoop(): never {
while (true) {}
}
// 联合类型
let id: string | number
id = 123 // ✅
id = 'abc' // ✅
id = true // ❌
// 交叉类型
type Person = { name: string }
type Employee = { employeeId: number }
type Worker = Person & Employee
const worker: Worker = {
name: '张三',
employeeId: 1001
}
// 字面量类型
let status: 'pending' | 'approved' | 'rejected'
status = 'pending' // ✅
status = 'approved' // ✅
status = 'cancelled' // ❌
// 类型别名
type Point = {
x: number
y: number
}
type ID = string | number
type Name = string
type Age = number类(Class)增强特性
1. 类的属性声明与类型注解
JavaScript
javascript
class Person {
constructor(name, age) {
this.name = name // 运行时动态添加属性
this.age = age
}
}
const p = new Person('张三', 25)
console.log(p.name) // "张三"
p.name = 123 // ✅ 可以赋任意类型TypeScript
typescript
class Person {
// 必须先声明属性及其类型
name: string
age: number
private id: string
protected salary: number
readonly birthYear: number
constructor(name: string, age: number, id: string) {
this.name = name
this.age = age
this.id = id
this.birthYear = new Date().getFullYear() - age
}
}
const p = new Person('张三', 25, 'P001')
p.name = '李四' // ✅
p.name = 123 // ❌ 类型错误
p.id = 'P002' // ❌ 私有属性,无法访问
p.birthYear = 1998 // ❌ 只读属性,无法修改2. 访问修饰符
TypeScript 提供了三种访问修饰符,JavaScript 仅支持私有字段(ES2022+)。
typescript
class Employee {
// public - 公有,任何地方可访问(默认)
public name: string
// private - 私有,只能在类内部访问
private salary: number
private _bonus: number = 0
// protected - 受保护,类内部和子类可访问
protected department: string
// readonly - 只读,初始化后不可修改
readonly employeeId: string
// ES2022+ 私有字段语法(编译后保持私有)
#secretCode: string = 'hidden'
constructor(
name: string,
salary: number,
department: string,
employeeId: string
) {
this.name = name
this.salary = salary
this.department = department
this.employeeId = employeeId
}
// getter
get bonus(): number {
return this._bonus
}
// setter
set bonus(value: number) {
if (value >= 0) {
this._bonus = value
} else {
console.error('奖金不能为负数')
}
}
// 私有方法
private calculateTax(): number {
return this.salary * 0.1
}
// 受保护方法
protected getDepartmentInfo(): string {
return `部门: ${this.department}`
}
// 公有方法
public getNetSalary(): number {
return this.salary - this.calculateTax() + this._bonus
}
}
class Manager extends Employee {
private teamSize: number
constructor(
name: string,
salary: number,
department: string,
employeeId: string,
teamSize: number
) {
super(name, salary, department, employeeId)
this.teamSize = teamSize
}
// 可以访问 protected 成员
public getManagerInfo(): string {
return `${this.getDepartmentInfo()}, 团队人数: ${this.teamSize}`
}
// ❌ 无法访问 private 成员
// getSalary() {
// return this.salary; // 错误
// }
}
const emp = new Employee('张三', 10000, '技术部', 'E001')
console.log(emp.name) // ✅ "张三"
// console.log(emp.salary); // ❌ 私有属性
// console.log(emp.department); // ❌ 受保护属性
emp.bonus = 5000 // ✅ 通过 setter 设置
console.log(emp.getNetSalary()) // ✅ 145003. 参数属性(简写语法)
TypeScript 允许在构造函数参数中直接声明并初始化属性:
typescript
// 传统写法
class Person1 {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 参数属性简写
class Person2 {
constructor(
public name: string,
public age: number,
private id: string,
protected email: string,
readonly createdAt: Date = new Date()
) {
// 自动创建并初始化属性,无需手动赋值
}
}
const p = new Person2('张三', 25, 'P001', 'zhangsan@example.com')
console.log(p.name) // "张三"
console.log(p.createdAt) // 当前日期4. 抽象类
TypeScript 支持抽象类,JavaScript 不支持:
typescript
// 抽象类 - 不能被实例化
abstract class Animal {
// 抽象属性
abstract name: string
// 抽象方法 - 子类必须实现
abstract makeSound(): void
// 具体方法 - 子类继承
public move(distance: number): void {
console.log(`${this.name} 移动了 ${distance} 米`)
}
// 受保护的抽象方法
protected abstract eat(): void
}
// 具体类必须实现所有抽象成员
class Dog extends Animal {
name: string
constructor(name: string) {
super()
this.name = name
}
makeSound(): void {
console.log('汪汪汪!')
}
protected eat(): void {
console.log(`${this.name} 正在吃狗粮`)
}
// 可以添加新方法
public fetch(): void {
console.log(`${this.name} 正在捡球`)
}
}
class Cat extends Animal {
name: string
constructor(name: string) {
super()
this.name = name
}
makeSound(): void {
console.log('喵喵喵!')
}
protected eat(): void {
console.log(`${this.name} 正在吃鱼`)
}
}
// const animal = new Animal(); // ❌ 无法创建抽象类实例
const dog = new Dog('旺财')
dog.makeSound() // "汪汪汪!"
dog.move(10) // "旺财 移动了 10 米"
dog.fetch() // "旺财 正在捡球"
const cat = new Cat('咪咪')
cat.makeSound() // "喵喵喵!"5. 类实现接口
typescript
// 定义接口
interface IAnimal {
name: string
age: number
makeSound(): void
}
interface IRunnable {
run(speed: number): void
}
interface ISwimmable {
swim(distance: number): void
}
// 实现单个接口
class Fish implements IAnimal {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
makeSound(): void {
console.log('咕噜咕噜...')
}
}
// 实现多个接口
class Duck implements IAnimal, IRunnable, ISwimmable {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
makeSound(): void {
console.log('嘎嘎嘎!')
}
run(speed: number): void {
console.log(`${this.name} 以 ${speed} km/h 的速度奔跑`)
}
swim(distance: number): void {
console.log(`${this.name} 游了 ${distance} 米`)
}
}
// 类可以同时继承和实现接口
abstract class BaseAnimal implements IAnimal {
abstract name: string
abstract age: number
abstract makeSound(): void
public sleep(hours: number): void {
console.log(`${this.name} 睡了 ${hours} 小时`)
}
}
class Lion extends BaseAnimal implements IRunnable {
name: string
age: number
constructor(name: string, age: number) {
super()
this.name = name
this.age = age
}
makeSound(): void {
console.log('吼!!!')
}
run(speed: number): void {
console.log(`狮子 ${this.name} 以 ${speed} km/h 奔跑`)
}
}6. 泛型类
typescript
// 泛型类
class Container<T> {
private items: T[] = []
add(item: T): void {
this.items.push(item)
}
get(index: number): T | undefined {
return this.items[index]
}
getAll(): T[] {
return [...this.items]
}
remove(index: number): T | undefined {
return this.items.splice(index, 1)[0]
}
get count(): number {
return this.items.length
}
}
// 使用泛型类
const numberContainer = new Container<number>()
numberContainer.add(1)
numberContainer.add(2)
numberContainer.add(3)
console.log(numberContainer.get(0)) // 1
const stringContainer = new Container<string>()
stringContainer.add('hello')
stringContainer.add('world')
console.log(stringContainer.getAll()) // ["hello", "world"]
// 多类型参数的泛型类
class Pair<K, V> {
constructor(
public key: K,
public value: V
) {}
public toString(): string {
return `${String(this.key)}: ${String(this.value)}`
}
}
const pair = new Pair<string, number>('age', 25)
console.log(pair.toString()) // "age: 25"
// 泛型约束
interface Lengthwise {
length: number
}
class Collection<T extends Lengthwise> {
private items: T[] = []
add(item: T): void {
this.items.push(item)
}
getTotalLength(): number {
return this.items.reduce((sum, item) => sum + item.length, 0)
}
}
const stringCollection = new Collection<string>()
stringCollection.add('hello') // string 有 length 属性
stringCollection.add('world')
console.log(stringCollection.getTotalLength()) // 10
// const numberCollection = new Collection<number>(); // ❌ number 没有 length 属性7. 类的类型
typescript
class Point {
x: number
y: number
constructor(x: number, y: number) {
this.x = x
this.y = y
}
add(point: Point): Point {
return new Point(this.x + point.x, this.y + point.y)
}
}
// 类作为类型使用
function printPoint(p: Point): void {
console.log(`(${p.x}, ${p.y})`)
}
const p1 = new Point(1, 2)
const p2 = new Point(3, 4)
const p3 = p1.add(p2)
printPoint(p3) // "(4, 6)"
// 类表达式
const Person = class {
constructor(public name: string) {}
greet() {
return `Hello, I'm ${this.name}`
}
}
const person = new Person('张三')
console.log(person.greet()) // "Hello, I'm 张三"
// 构造签名
interface IPointConstructor {
new (x: number, y: number): Point
}
function createPoint(ctor: IPointConstructor, x: number, y: number): Point {
return new ctor(x, y)
}
const point = createPoint(Point, 5, 10)
console.log(point) // Point { x: 5, y: 10 }8. this 类型
typescript
class Calculator {
protected value: number = 0
add(n: number): this {
this.value += n
return this // 返回 this 实现链式调用
}
subtract(n: number): this {
this.value -= n
return this
}
multiply(n: number): this {
this.value *= n
return this
}
divide(n: number): this {
if (n !== 0) {
this.value /= n
}
return this
}
getValue(): number {
return this.value
}
}
const calc = new Calculator()
const result = calc
.add(10) // this.value = 10
.multiply(2) // this.value = 20
.subtract(5) // this.value = 15
.divide(3) // this.value = 5
.getValue()
console.log(result) // 5
// this 类型在继承中的应用
class ScientificCalculator extends Calculator {
sin(): this {
this.value = Math.sin(this.value)
return this
}
cos(): this {
this.value = Math.cos(this.value)
return this
}
pow(exponent: number): this {
this.value = Math.pow(this.value, exponent)
return this
}
}
const sciCalc = new ScientificCalculator()
sciCalc
.add(1)
.multiply(Math.PI / 2)
.sin() // 返回类型仍然是 ScientificCalculator
.getValue()接口(Interface)
TypeScript 的接口是其核心特性之一,JavaScript 没有对应概念。
1. 基本接口
typescript
// 定义对象形状
interface Person {
name: string
age: number
email?: string // 可选属性
readonly id: string // 只读属性
}
const person: Person = {
name: '张三',
age: 25,
id: 'P001'
// email 是可选的,可以不提供
}
person.name = '李四' // ✅
// person.id = "P002"; // ❌ 只读属性不可修改
// 索引签名
interface StringArray {
[index: number]: string
}
const arr: StringArray = ['a', 'b', 'c']
console.log(arr[0]) // "a"
interface StringDictionary {
[key: string]: string | number
name: string // 必须符合索引签名类型
age: number // ✅ number 是 string | number 的子类型
}
const dict: StringDictionary = {
name: '张三',
age: 25,
city: '北京'
}2. 函数类型接口
typescript
// 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean
}
const mySearch: SearchFunc = (src, sub) => {
return src.indexOf(sub) !== -1
}
console.log(mySearch('hello world', 'world')) // true
// 构造签名
interface IAnimalConstructor {
new (name: string): IAnimal
}
interface IAnimal {
name: string
makeSound(): void
}
class Dog implements IAnimal {
constructor(public name: string) {}
makeSound(): void {
console.log('汪汪汪!')
}
}
function createAnimal(ctor: IAnimalConstructor, name: string): IAnimal {
return new ctor(name)
}
const dog = createAnimal(Dog, '旺财')
dog.makeSound() // "汪汪汪!"3. 接口继承
typescript
// 单继承
interface Shape {
color: string
}
interface Square extends Shape {
sideLength: number
}
const square: Square = {
color: 'red',
sideLength: 10
}
// 多继承
interface Printable {
print(): void
}
interface Loggable {
log(): void
}
interface Document extends Printable, Loggable {
content: string
}
class PDFDocument implements Document {
constructor(public content: string) {}
print(): void {
console.log(`打印: ${this.content}`)
}
log(): void {
console.log(`日志: ${this.content}`)
}
}
// 接口继承类
class Control {
private state: any
protected getState(): any {
return this.state
}
}
// SelectableControl 继承了 Control 的私有成员
interface SelectableControl extends Control {
select(): void
}
// 只有 Control 或其子类才能实现 SelectableControl
class Button extends Control implements SelectableControl {
select(): void {
console.log('按钮被选中')
}
}
class TextBox extends Control implements SelectableControl {
select(): void {
console.log('文本框被选中')
}
}4. 接口与类型别名的区别
typescript
// 接口
interface Person {
name: string
age: number
}
// 接口可以被类实现
class Student implements Person {
name: string
age: number
grade: string
constructor(name: string, age: number, grade: string) {
this.name = name
this.age = age
this.grade = grade
}
}
// 接口可以声明合并
interface Person {
email: string
}
// 现在 Person 有三个属性
const p: Person = {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
}
// 类型别名
type Point = {
x: number
y: number
}
// 类型别名可以表示原始类型、联合类型、元组等
type ID = string | number
type Name = string
type Coordinate = [number, number]
// 类型别名不能被类实现
// class PointClass implements Point {} // ❌ 不能实现类型别名
// 类型别名不能声明合并
type Point = {
z: number // ❌ 重复的标识符
}泛型(Generics)
1. 泛型函数
typescript
// 泛型函数
function identity<T>(arg: T): T {
return arg
}
// 使用方式
const str = identity<string>('hello') // 显式指定类型
const num = identity(123) // 类型推断
// 泛型数组
function getFirst<T>(arr: T[]): T | undefined {
return arr[0]
}
console.log(getFirst([1, 2, 3])) // 1
console.log(getFirst(['a', 'b', 'c'])) // "a"
// 多类型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value]
}
const p = pair<string, number>('age', 25)
console.log(p) // ["age", 25]
// 泛型约束
interface Lengthwise {
length: number
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}
logLength('hello') // 5
logLength([1, 2, 3]) // 3
logLength({ length: 10 }) // 10
// logLength(123); // ❌ number 没有 length 属性
// 在泛型约束中使用类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const obj = { name: '张三', age: 25 }
console.log(getProperty(obj, 'name')) // "张三"
// console.log(getProperty(obj, "email")); // ❌ "email" 不是 obj 的属性2. 泛型接口
typescript
// 泛型接口
interface Container<T> {
value: T
getValue(): T
setValue(value: T): void
}
class Box<T> implements Container<T> {
value: T
constructor(initialValue: T) {
this.value = initialValue
}
getValue(): T {
return this.value
}
setValue(value: T): void {
this.value = value
}
}
const stringBox = new Box<string>('hello')
console.log(stringBox.getValue()) // "hello"
const numberBox = new Box<number>(42)
console.log(numberBox.getValue()) // 42
// 函数类型的泛型接口
interface IGenericFunc<T> {
(arg: T): T
}
const genericIdentity: IGenericFunc<number> = arg => arg
console.log(genericIdentity(123)) // 1233. 泛型约束与默认值
typescript
// 泛型默认类型
interface ApiResponse<T = any> {
code: number
message: string
data: T
}
const response1: ApiResponse = {
code: 200,
message: 'success',
data: { name: '张三' }
}
const response2: ApiResponse<string> = {
code: 200,
message: 'success',
data: '操作成功'
}
// 多个类型参数的默认值
interface Page<T = any, U = number> {
items: T[]
total: U
page: U
pageSize: U
}
const page: Page<{ id: number; name: string }> = {
items: [{ id: 1, name: '张三' }],
total: 100,
page: 1,
pageSize: 10
}
// 泛型约束中使用类类型
class BeeKeeper {
hasMask: boolean = true
}
class ZooKeeper {
nametag: string = '管理员'
}
class Animal {
numLegs: number = 4
}
class Bee extends Animal {
keeper: BeeKeeper = new BeeKeeper()
numLegs: number = 6
}
class Lion extends Animal {
keeper: ZooKeeper = new ZooKeeper()
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c()
}
const bee = createInstance(Bee)
console.log(bee.keeper.hasMask) // true
const lion = createInstance(Lion)
console.log(lion.keeper.nametag) // "管理员"高级类型
1. 交叉类型与联合类型
typescript
// 联合类型
type StringOrNumber = string | number
function formatValue(value: StringOrNumber): string {
if (typeof value === 'string') {
return value.toUpperCase()
} else {
return value.toFixed(2)
}
}
// 交叉类型
type Name = { name: string }
type Age = { age: number }
type Email = { email: string }
type Person = Name & Age & Email
const person: Person = {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
}
// 混合使用
type Mixed = { id: number } & (string | { name: string })
// 等同于 { id: number } & string 或 { id: number } & { name: string }2. 类型守卫与类型收窄
typescript
// typeof 类型守卫
function printValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase()) // value 是 string
} else {
console.log(value.toFixed(2)) // value 是 number
}
}
// instanceof 类型守卫
class Dog {
bark() {
console.log('汪汪汪!')
}
}
class Cat {
meow() {
console.log('喵喵喵!')
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark()
} else {
animal.meow()
}
}
// in 操作符类型守卫
interface Bird {
fly(): void
layEggs(): void
}
interface Fish {
swim(): void
layEggs(): void
}
function move(animal: Bird | Fish) {
if ('fly' in animal) {
animal.fly() // animal 是 Bird
} else {
animal.swim() // animal 是 Fish
}
}
// 自定义类型守卫
interface Car {
car: string
drive(): void
}
interface Boat {
boat: string
sail(): void
}
function isCar(vehicle: Car | Boat): vehicle is Car {
return (vehicle as Car).car !== undefined
}
function operate(vehicle: Car | Boat) {
if (isCar(vehicle)) {
vehicle.drive() // vehicle 是 Car
} else {
vehicle.sail() // vehicle 是 Boat
}
}
// 可辨识联合
interface Circle {
kind: 'circle'
radius: number
}
interface Rectangle {
kind: 'rectangle'
width: number
height: number
}
interface Triangle {
kind: 'triangle'
base: number
height: number
}
type Shape = Circle | Rectangle | Triangle
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rectangle':
return shape.width * shape.height
case 'triangle':
return (shape.base * shape.height) / 2
}
}
// 穷尽检查
function assertNever(x: never): never {
throw new Error('Unexpected object: ' + x)
}
function getAreaExhaustive(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rectangle':
return shape.width * shape.height
case 'triangle':
return (shape.base * shape.height) / 2
default:
return assertNever(shape) // 如果新增类型会报错
}
}3. 映射类型
typescript
// Readonly 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
// Partial 映射类型
type Partial<T> = {
[P in keyof T]?: T[P]
}
// Required 映射类型
type Required<T> = {
[P in keyof T]-?: T[P]
}
// Pick 映射类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
// Omit 映射类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
// Record 映射类型
type Record<K extends keyof any, T> = {
[P in K]: T
}
// 使用示例
interface User {
id: number
name: string
email: string
age?: number
}
// 所有属性只读
type ReadonlyUser = Readonly<User>
// { readonly id: number; readonly name: string; readonly email: string; readonly age?: number; }
// 所有属性可选
type PartialUser = Partial<User>
// { id?: number; name?: string; email?: string; age?: number; }
// 所有属性必选
type RequiredUser = Required<User>
// { id: number; name: string; email: string; age: number; }
// 选择部分属性
type UserNameAndEmail = Pick<User, 'name' | 'email'>
// { name: string; email: string; }
// 排除部分属性
type UserWithoutId = Omit<User, 'id'>
// { name: string; email: string; age?: number; }
// 创建对象类型
type UserRoles = Record<'admin' | 'user' | 'guest', string[]>
// { admin: string[]; user: string[]; guest: string[]; }4. 条件类型
typescript
// 基本条件类型
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// 内置条件类型
type NonNullable<T> = T extends null | undefined ? never : T
type C = NonNullable<string | null | undefined> // string
type Exclude<T, U> = T extends U ? never : T
type D = Exclude<'a' | 'b' | 'c', 'a'> // "b" | "c"
type Extract<T, U> = T extends U ? T : never
type E = Extract<'a' | 'b' | 'c', 'a' | 'f'> // "a"
type ReturnType<T> = T extends (...args: any) => infer R ? R : any
type F = ReturnType<() => string> // string
type Parameters<T> = T extends (...args: infer P) => any ? P : never
type G = Parameters<(a: string, b: number) => void> // [string, number]
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never
type H = ToArray<string | number> // string[] | number[]
// 非分布式条件类型
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type I = ToArrayNonDist<string | number> // (string | number)[]5. 模板字面量类型
typescript
// 基本模板字面量类型
type World = 'world'
type Greeting = `hello ${World}` // "hello world"
// 联合类型展开
type EmailLocaleIDs = 'welcome_email' | 'email_heading'
type FooterLocaleIDs = 'footer_title' | 'footer_sendoff'
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
// 字符串操作类型
type UppercaseGreeting = Uppercase<Greeting> // "HELLO WORLD"
type LowercaseGreeting = Lowercase<Greeting> // "hello world"
type CapitalizeGreeting = Capitalize<Greeting> // "Hello world"
type UncapitalizeGreeting = Uncapitalize<Greeting> // "hello world"
// 类型推断
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface Person {
name: string
age: number
}
type PersonGetters = Getters<Person>
// {
// getName: () => string;
// getAge: () => number;
// }
// 移除前缀
type RemovePrefix<T, P extends string> = T extends `${P}${infer Rest}`
? Rest
: T
type J = RemovePrefix<'getUserName', 'get'> // "UserName"装饰器(Decorators)
TypeScript 支持装饰器,JavaScript 装饰器提案仍在进行中。
1. 类装饰器
typescript
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor)
Object.seal(constructor.prototype)
}
@sealed
class BankAccount {
balance: number = 0
deposit(amount: number) {
this.balance += amount
}
}
// 类装饰器工厂
function logger(prefix: string) {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(...args)
console.log(`${prefix}: 创建实例,参数: ${JSON.stringify(args)}`)
}
}
}
}
@logger('BankAccount')
class BankAccount2 {
constructor(public balance: number) {}
}
const account = new BankAccount2(1000)
// 输出: BankAccount: 创建实例,参数: [1000]2. 方法装饰器
typescript
// 方法装饰器
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value
}
}
class Person {
constructor(private name: string) {}
@enumerable(false)
getSecret() {
return `秘密: ${this.name}`
}
}
// 方法装饰器 - 日志
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`调用 ${propertyKey},参数: ${JSON.stringify(args)}`)
const result = originalMethod.apply(this, args)
console.log(`结果: ${JSON.stringify(result)}`)
return result
}
return descriptor
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b
}
}
const calc = new Calculator()
calc.add(2, 3)
// 输出:
// 调用 add,参数: [2,3]
// 结果: 53. 属性装饰器
typescript
// 属性装饰器
function defaultValue(value: any) {
return function (target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
value: value,
enumerable: true,
configurable: true,
writable: true
})
}
}
class Config {
@defaultValue('localhost')
host!: string
@defaultValue(8080)
port!: number
}
const config = new Config()
console.log(config.host) // "localhost"
console.log(config.port) // 80804. 参数装饰器
typescript
// 参数装饰器
function required(target: any, propertyKey: string, parameterIndex: number) {
const existingRequiredParameters: number[] =
Reflect.getOwnMetadata('required', target, propertyKey) || []
existingRequiredParameters.push(parameterIndex)
Reflect.defineMetadata(
'required',
existingRequiredParameters,
target,
propertyKey
)
}
function validate(
target: any,
propertyName: string,
descriptor: TypedPropertyDescriptor<Function>
) {
const method = descriptor.value!
descriptor.value = function () {
const requiredParameters: number[] =
Reflect.getOwnMetadata('required', target, propertyName) || []
for (const parameterIndex of requiredParameters) {
if (
parameterIndex >= arguments.length ||
arguments[parameterIndex] === undefined
) {
throw new Error(`参数 ${parameterIndex + 1} 是必需的`)
}
}
return method.apply(this, arguments)
}
}
class UserService {
@validate
createUser(@required name: string, @required email: string, age?: number) {
console.log(`创建用户: ${name}, ${email}, ${age}`)
}
}
const service = new UserService()
service.createUser('张三', 'zhangsan@example.com', 25) // ✅
// service.createUser("张三", undefined); // ❌ 抛出错误5. 装饰器元数据
typescript
import 'reflect-metadata'
const formatMetadataKey = Symbol('format')
function format(formatString: string) {
return Reflect.metadata(formatMetadataKey, formatString)
}
function getFormat(target: any, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey)
}
class Person {
@format('Hello, %s')
name: string
constructor(name: string) {
this.name = name
}
}
const person = new Person('张三')
const formatString = getFormat(person, 'name')
console.log(formatString) // "Hello, %s"模块系统
1. 模块导出
typescript
// module.ts
// 命名导出
export const PI = 3.14159
export interface Point {
x: number
y: number
}
export class Circle {
constructor(public radius: number) {}
getArea(): number {
return PI * this.radius ** 2
}
}
export function calculateDistance(p1: Point, p2: Point): number {
return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
}
// 默认导出
export default class Rectangle {
constructor(
public width: number,
public height: number
) {}
getArea(): number {
return this.width * this.height
}
}
// 重命名导出
export { Circle as CircleClass }
export { calculateDistance as distance }
// 重新导出
export * from './shapes'
export { Triangle } from './shapes'2. 模块导入
typescript
// main.ts
// 导入默认导出
import Rectangle from './module'
// 导入命名导出
import { PI, Point, Circle, calculateDistance } from './module'
// 重命名导入
import { Circle as MyCircle } from './module'
// 导入所有
import * as Shapes from './module'
// 仅导入副作用
import './module'
// 使用
const circle = new Circle(5)
console.log(circle.getArea())
const rect = new Rectangle(10, 20)
console.log(rect.getArea())
const p1: Point = { x: 0, y: 0 }
const p2: Point = { x: 3, y: 4 }
console.log(calculateDistance(p1, p2)) // 53. 命名空间
typescript
// 命名空间(用于组织代码)
namespace Geometry {
export interface Point {
x: number
y: number
}
export class Circle {
constructor(
public center: Point,
public radius: number
) {}
getArea(): number {
return Math.PI * this.radius ** 2
}
}
export class Rectangle {
constructor(
public topLeft: Point,
public width: number,
public height: number
) {}
getArea(): number {
return this.width * this.height
}
}
// 非导出成员在命名空间外部不可见
function helper(): void {
console.log('helper function')
}
}
const center: Geometry.Point = { x: 0, y: 0 }
const circle = new Geometry.Circle(center, 5)
console.log(circle.getArea())
// 命名空间合并
namespace Geometry {
export interface Point3D extends Point {
z: number
}
export class Sphere {
constructor(
public center: Point3D,
public radius: number
) {}
getVolume(): number {
return (4 / 3) * Math.PI * this.radius ** 3
}
}
}
const center3D: Geometry.Point3D = { x: 0, y: 0, z: 0 }
const sphere = new Geometry.Sphere(center3D, 5)
console.log(sphere.getVolume())4. 模块解析
typescript
// tsconfig.json 配置
{
"compilerOptions": {
"module": "commonjs", // 模块系统: commonjs, amd, es2015, esnext
"moduleResolution": "node", // 模块解析策略: node, classic
"baseUrl": "./src", // 模块解析基础路径
"paths": {
"@/*": ["*"], // 路径别名
"@components/*": ["components/*"],
"@utils/*": ["utils/*"]
}
}
}
// 使用路径别名
import { Button } from "@components/Button";
import { formatDate } from "@utils/date";其他重要特性
1. 类型推断
typescript
// 变量类型推断
let x = 10 // 推断为 number
let y = 'hello' // 推断为 string
let z = [1, 2, 3] // 推断为 number[]
let obj = { a: 1, b: 'text' } // 推断为 { a: number; b: string; }
// 函数返回值推断
function add(a: number, b: number) {
return a + b // 推断返回类型为 number
}
// 上下文类型推断
window.onmousedown = function (mouseEvent) {
// mouseEvent 推断为 MouseEvent
console.log(mouseEvent.button)
}
// 最佳通用类型
let mixed = [1, 'hello', true] // 推断为 (number | string | boolean)[]
// 类型断言
let someValue: any = 'this is a string'
let strLength1: number = (someValue as string).length
let strLength2: number = (<string>someValue).length
// 非空断言
function processValue(value: string | null) {
// 使用 ! 断言 value 不为 null
console.log(value!.toUpperCase())
}
// 确定赋值断言
let value!: number
initialize()
console.log(value) // ✅ 使用 ! 断言已赋值
function initialize() {
value = 10
}2. 类型兼容性
typescript
// 结构化类型系统(鸭子类型)
interface Point {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
const point3D: Point3D = { x: 1, y: 2, z: 3 }
const point: Point = point3D // ✅ Point3D 有 Point 的所有属性
// 函数参数双向协变
let fn1 = (a: number, b: string) => {}
let fn2 = (a: number) => {}
fn2 = fn1 // ✅ 参数少的可以赋给参数多的
// fn1 = fn2; // ❌ 参数多的不能赋给参数少的
// 返回值协变
let fn3 = (): string => 'hello'
let fn4 = (): 'hello' => 'hello'
fn3 = fn4 // ✅ 返回值是子类型
// 枚举兼容性
enum Color {
Red,
Green,
Blue
}
enum Status {
Active,
Inactive
}
let color: Color = Color.Red
let status: Status = Status.Active
// color = status; // ❌ 不同枚举类型不兼容
// 类兼容性
class Animal {
name: string = ''
}
class Dog extends Animal {
breed: string = ''
}
let animal: Animal = new Dog() // ✅ 子类可以赋给父类
class Person {
name: string = ''
private age: number = 0
}
class Employee {
name: string = ''
private age: number = 0
}
// let p: Person = new Employee(); // ❌ 私有成员来源不同,不兼容3. 声明文件
typescript
// global.d.ts - 全局声明文件
declare var jQuery: (selector: string) => any
declare function $(selector: string): any
declare namespace MyLib {
interface Options {
debug: boolean
}
function init(options: Options): void
}
// module.d.ts - 模块声明文件
declare module 'my-library' {
export interface Config {
apiKey: string
timeout?: number
}
export function initialize(config: Config): void
export class Client {
constructor(config: Config)
getData(): Promise<any>
}
export default Client
}
// 使用
import Client, { initialize, Config } from 'my-library'
const config: Config = {
apiKey: 'your-api-key',
timeout: 5000
}
initialize(config)
const client = new Client(config)4. 类型操作符
typescript
// keyof 操作符
interface Person {
name: string
age: number
email: string
}
type PersonKeys = keyof Person // "name" | "age" | "email"
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
// typeof 操作符
const person = {
name: '张三',
age: 25
}
type PersonType = typeof person // { name: string; age: number; }
// 索引访问类型
type NameType = Person['name'] // string
type AgeType = Person['age'] // number
// const 断言
const config = {
host: 'localhost',
port: 8080
} as const
// 类型变为 { readonly host: "localhost"; readonly port: 8080; }
const arr = [1, 2, 3] as const
// 类型变为 readonly [1, 2, 3]
// satisfies 操作符(TypeScript 4.9+)
type Colors = 'red' | 'green' | 'blue'
const theme = {
primary: 'red',
secondary: 'green'
} satisfies Record<string, Colors>
// theme.primary 类型为 "red",而不是 Colors5. const 类型参数
typescript
// TypeScript 5.0+
function createArray<T extends readonly string[]>(...items: T): T {
return items
}
const arr1 = createArray('a', 'b', 'c')
// 类型为 ["a", "b", "c"]
const arr2 = createArray<string>('a', 'b', 'c')
// 类型为 string[]
// 使用 const 类型参数
function createArrayConst<const T extends readonly string[]>(...items: T): T {
return items
}
const arr3 = createArrayConst('a', 'b', 'c')
// 类型为 readonly ["a", "b", "c"]版本演进对比
ES6+ 特性在 TypeScript 中的支持
| 特性 | JavaScript | TypeScript |
|---|---|---|
| let/const | ES6 | ✅ 完全支持 |
| 箭头函数 | ES6 | ✅ 完全支持 + 类型注解 |
| 类 | ES6 | ✅ 增强支持(修饰符、抽象类等) |
| 模块 | ES6 | ✅ 完全支持 + 命名空间 |
| 解构赋值 | ES6 | ✅ 完全支持 + 类型注解 |
| 展开运算符 | ES6 | ✅ 完全支持 |
| Promise | ES6 | ✅ 完全支持 + async/await |
| async/await | ES2017 | ✅ 完全支持 |
可选链 ?. | ES2020 | ✅ 完全支持 |
空值合并 ?? | ES2020 | ✅ 完全支持 |
私有字段 # | ES2022 | ✅ 支持 + private 修饰符 |
TypeScript 独有特性
| 特性 | 说明 |
|---|---|
| 静态类型 | 编译时类型检查 |
| 接口 | 定义对象形状 |
| 泛型 | 类型参数化 |
| 装饰器 | 元编程支持 |
| 枚举 | 命名常量集合 |
| 命名空间 | 代码组织方式 |
| 类型别名 | 类型重命名 |
| 联合/交叉类型 | 类型组合 |
| 类型守卫 | 运行时类型检查 |
| 条件类型 | 类型级别条件判断 |
| 映射类型 | 类型转换 |
| 模板字面量类型 | 字符串类型操作 |
编译目标配置
json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022", // 编译目标版本
"module": "ESNext", // 模块系统
"lib": ["ES2022", "DOM"], // 包含的库声明
"strict": true, // 严格模式
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true, // 严格空值检查
"strictFunctionTypes": true, // 严格函数类型检查
"noImplicitThis": true, // 禁止隐式 this
"alwaysStrict": true, // 总是使用严格模式
"noUnusedLocals": true, // 检查未使用的变量
"noUnusedParameters": true, // 检查未使用的参数
"noImplicitReturns": true, // 检查每个分支都有返回值
"noFallthroughCasesInSwitch": true // 检查 switch 穿透
}
}总结
TypeScript 是 JavaScript 的超集,在保持 JavaScript 灵活性的同时,增加了强大的类型系统和面向对象特性。主要区别包括:
- 类型系统:TypeScript 提供静态类型检查,在编译时发现潜在错误
- 类增强:支持访问修饰符、抽象类、参数属性、泛型类等
- 接口:定义对象形状,支持继承、合并、实现
- 泛型:类型参数化,提高代码复用性
- 高级类型:联合类型、交叉类型、条件类型、映射类型等
- 装饰器:元编程支持,增强类、方法、属性、参数
- 模块系统:增强的模块支持,包括命名空间和路径别名
这些特性使得 TypeScript 更适合大型项目开发,提供更好的代码提示、重构支持和错误检测。