TS & JS 区别?
ts
是 js 的超集
- 特性
- 类型批注
- 编译时类型检查
- 类型推断
- 类型擦除
- 泛型编程
type
1. 原始类型的别名
ini
type ID = number;
type Name = string;
type IsActive = boolean;
const userId: ID = 123;
const userName: Name = "Alice";
2. 对象类型的别名
ini
type User = {
id: number;
name: string;
email?: string;
};
const user: User = {
id: 1,
name: "Bob"
};
3. 联合类型(Union Types)
满足多个类型中的任意一个类型,"或" T | U
ini
type Status = "active" | "inactive" | "pending";
type ID = number | string;
const status: Status = "active"; // 必须是其中之一
const userId: ID = "abc123"; // 可以是 number 或 string
用于限制值的范围,实现类型安全的枚举式字段。
- 获取联合类型的key 1,取值
ts
enum Direction {
left,
top,
right = 20,
bottom
}
type D = keyof typeof Direction // type D = "left" | "top" | "right" | "bottom"
- 获取联合类型的key 2, 使用
keyof
+ 联合类型
ini
type User = { id: number; name: string }
type Product = { id: number; price: number }
type Data = User | Product
// 获取所有可能的 key
type Keys1 = keyof Data
// 等价于:
type Keys2 = keyof User | keyof Product
// 等价于:
type Keys3 = keyof (User | Product)
4. 交叉类型(Intersection Types)
将多个类型合并为一个类型,"并" T & U
ini
type Name = {
name: string;
};
type Age = {
age: number;
};
type Person = Name & Age;
const person: Person = {
name: "Charlie",
age: 25
};
将多个类型合并为一个,所有字段都必须存在。
5. 元组类型(Tuple)
ts
type Coordinates = [string, number];
type RGB = [number, string, number];
const point: Coordinates = ['sss', 20];
const color: RGB = [255, 'dasd', 0];
6. 函数类型
typescript
type GreetFunction = (name: string) => string;
const greet: GreetFunction = (name) => `Hello, ${name}`;
7. 泛型类型别名
css
type Box<T> = {
value: T;
};
const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };
8. 类型别名
使用 type
关键字为一个类型起一个"别名"。 type 别名 = 类型定义
typescript
type T = string | number // T 为类型别名
interface
interface
(接口)是定义对象结构的核心机制之一。用于描述对象的形状(shape),包括属性、方法、索引签名等。
typescript
interface Animal {
readonly name: string
age: number | null
sex?: string
set: (key: string, val: string | number) => void // ✅ 方法签名
get?: () => string // ✅ 函数类型接口
speak(): void
eat?(food: string): string
}
interface 继承
ts
interface Animal {
sex?: string
say: () => void
}
interface Person {
name: string
age: number | null
}
interface Dog extends Animal, Person {
height: string
}
const d: Dog = {} // ❌ 错误,类型"{}"缺少类型"Dog"中的以下属性: height, say, name, age
混合接口(同时有属性和方法)
ts
interface Couter {
(start: string): string
interval: number
reset(): void
}
const initCouter = function (start: string) {
return start + ''
} as Couter
initCouter.interval = 1000
initCouter.reset = () => {
/* ... */
}}
interface 和 type 区别
interface
vs type
特性 | interface |
type |
---|---|---|
定义对象结构 | ✅ 推荐 | ✅ |
支持继承 | ✅ extends |
✅ 使用交叉类型 & |
支持声明合并 | ✅ | ❌ |
可定义联合类型 | ❌ | ✅ |
可定义元组、函数类型 | ❌ | ✅ |
可用于条件类型 | ❌ | ✅ |
扩展性 | 更好(适合长期演进) | 固定定义 |
- 优先使用
interface
描述对象和类的结构。 - 使用
type
定义联合类型、复杂类型、映射类型等。
Class 类
class
(类)是面向对象编程的核心,它在 JavaScript 的 class
基础上增加了 类型注解、访问修饰符、抽象类、静态类型检查 等特性
基本使用
ts
class Person {
public name: string
protected age: number
protected sex?: string
private address?: string
constructor(name: string, age: number) {
this.name = name
this.age = age
}
say(): string {
const d = `Hello, 我是 ${this.name}, 今年${this.age}岁`
return d
}
}
const person = new Person('Rubin', 25)
person.say() // ✅ "Hello, 我是Rubin, 今年25岁"
Person.sex // ❌ 错误,类型"typeof Person"上不存在属性"sex"。
Person.age // ❌ 错误,类型"typeof Person"上不存在属性"age"。
类的初始参数简写
ts
class Person {
constructor(public readonly name: string, private age: number) { }
}
const p = new Person('runbin', 33)
p.name
p.age // ❌
类的继承
typescript
class Person {
public name: string
protected age: number
protected sex?: string
constructor(name: string, age: number, sex: string) {
this.name = name
this.age = age
this.sex = sex
}
say(): string {
const d = `Hello, 我是 ${this.name}, 今年${this.age}岁`
return d
}
}
class Father extends Person {
height: number
constructor(name: string, age: number, height: number) {
super(name, age) // 调用父类构造函数
this.height = height
}
say(): string {
super.say()
const d = `Hello, 我是 ${this.name}, 今年${this.age}岁,性别` + this.sex
return d
}
}
const father = new Father('Rubin', 25, 188, '男')
father.say() // ✅ "Hello, 我是Rubin, 今年25岁,性别男"
修饰符 public private protected readonly
类属性修饰符(Class Property Modifiers)用于控制类成员(属性和方法)的访问权限、行为和类型。它们是实现封装、继承和类型安全的重要工具。
public
可以在类内部、子类、外部实例中访问。private
只能在定义它的类内部访问,子类和外部实例都不能访问。protected
类内部 + 子类中可访问,外部实例不能访问。readonly
只读,属性只能在声明时或构造函数中初始化,之后不能修改。static
- 静态,属于类本身 ,不属于任何实例。通过类名.属性
访问,不能通过this
或实例访问。
ts
class Person {
public name: string
protected age: number
protected sex?: string
private readonly address = '北京'
static P1 = 'P1P1P1'
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const person = new Person('小明', 19)
person.name // ✅
Person.name // ✅
Person.age // ❌ 错误,类型"typeof Person"上不存在属性"age"。
Person.sex // ❌ 错误,类型"typeof Person"上不存在属性"sex"。
person.P1 // ✅
Person.P1 // ❌ 错误,属性"P1"在类型"Person"上不存在
类新的私有属性提案:"#属性"
ts
class Person {
#address = '北京'
}
const p = new Person()
p['#address'] // ❌ 错误,类型"Person"上不存在属性"#address"
抽象类
abstract
(抽象)是一个关键字,用于定义 抽象类(Abstract Class) 和 抽象方法(Abstract Method) 。它用于创建只能被继承、不能被直接实例化的基类,常用于面向对象编程中定义接口契约和共享逻辑。
ts
abstract class Animal {
abstract say(): void
}
// ❌ 子类如不实现say函数,就报错:非抽象类"Person"不会实现继承自"Animal"类的抽象成员 say。
class Person extends Animal {
nosay() {
console.log(';abstract')
}
}
基础类型
1. string 类型 - 字符串
ts
let userName: string = "Alice";
let greeting: string = `Hello, ${userName}!`;
console.log(greeting); // 输出: Hello, Alice!
2. number 类型 - 数字(整数、浮点数、NaN、Infinity)
ts
let age: number = 25;
let price: number = 99.99;
let score: number = NaN;
let bigNumber: number = Infinity;
console.log(age, price, score, bigNumber);
3. boolean 类型 - 布尔值
ts
let isActive: boolean = true;
let isLoggedIn: boolean = false;
console.log(isActive); // true
4. null 类型 - 空值
ts
let car: string | null = null; // 注意:null 通常与联合类型一起使用
car = "Toyota";
console.log(car);
5. undefined 类型 - 未定义
ts
let user: { name?: string } = {};
let nickname: string | undefined = user.name;
console.log(nickname); // undefined
6. symbol 类型 - 唯一标识符(ES6)
ts
const id1: symbol = Symbol("id");
const id2: symbol = Symbol("id");
console.log(id1 === id2); // false(每个 symbol 都是唯一的)
7. bigint 类型 - 大整数(ES2020)
ts
const bigInt2: bigint = BigInt(456);
console.log(bigInt1 + bigInt2); // 579n
8. any 类型 - 任意类型(不推荐滥用)
ts
let anything: any = "hello";
anything = 100;
anything = true;
console.log(anything); // true
9. unknown 类型 - 未知类型(比 any 更安全)
ts
let userInput: unknown = "hello";
userInput = 10;
// 必须进行类型检查才能使用
if (typeof userInput === "number") {
console.log(userInput * 2);
} else {
console.log("Not a number");
}
10. void 类型 - 无返回值
ts
function logMessage(message: string): void {
console.log(message);
}
logMessage("This function returns nothing.");
11. never 类型 - 永不返回(如抛出异常或死循环)
ts
function throwError(message: string): never {
throw new Error(message);
}
// function infiniteLoop(): never {
// while (true) {}
// }
12. 字面量类型 - 精确值
ts
type Direction = "left" | "right" | "up" | "down";
let move: Direction = "left";
// move = "forward"; // ❌ 错误:不在允许范围内
type StatusCode = 200 | 404 | 500;
let status: StatusCode = 200;
13. 数组类型
ts
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Alice", "Bob"];
14. 元组类型 - 固定长度和类型的数组
ts
let person: [string, number] = ["Charlie", 30];
console.log(person[0]); // "Charlie"
console.log(person[1]); // 30
15. 枚举类型(enum)
ts
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
let favoriteColor: Color = Color.Green;
console.log(favoriteColor); // "GREEN"
- 数字枚举
ts
enum Direction {
left,
top,
right = 20,
bottom
}
const left = Direction.left // 0
const top = Direction.top // 1
const right = Direction.right // 20
const bottom = Direction.bottom // 21
console.log(left, top, right, bottom) // ✅ 0 1 20 21
- 异构枚举
ts
enum WR {
no = 100,
yes = "yes"
}
const no = WR.no // 100
const yes = WR.yes // "yes"
console.log(no, yes) // ✅ 100, "yes"
16. 类型推断(Type Inference)
ts
let country = "China"; // 自动推断为 string
let temperature = 25.5; // 自动推断为 number
let isLoading = true; // 自动推断为 boolean
17. 使用 typeof 获取值的类型(在类型层面)
js
const userObj = { name: "David", age: 28 };
type User = typeof userObj;
// 等价于: type User = { name: string; age: number; }
const user2: User = { name: "Eve", age: 22 };
console.log(user2);
高级类型
1. keyof
获取对象类型的键名
keyof
获取所有键的联合。
ts
type User = {
id: number;
name: string;
email: string;
};
// 获取所有键的联合类型
type UserKeys = keyof User; // 结果:UserKeys 是 "id" | "name" | "email"
用法:类型索引
ts
function getInfo<User>(user: User, key: keyof User) {
return user[key]
}
getInfo({ name: 'B', age: 2 }, 'age') // ✅ OK
getInfo({ name: 'A', age: 1 }, 'ccc') // ❌ 错误,类型"ccc"的参数不能赋给类型"age"|"name"的参数
2. typeof
从值推导类型
ini
const user = {
name: "Alice",
age: 30
};
// 使用 typeof 获取 user 的结构类型
type UserType = typeof user;
// 等价于:
type UserType = {
name: string;
age: number;
}
const newUser: UserType = {
name: "Bob",
age: 25
};
用法:从配置对象自动推导类型
ts
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
withCredentials: true
};
type Config = typeof config;
function request<T>(url: string, options: Config): Promise<T> {
// ...
}
用法:联合类型判断(类型守卫)
ts
if (typeof value === "string") {
console.log(value.toUpperCase()); // TS 知道此时 value 是 string
}
3.类型约束
类型约束(Type Constraints) 是指通过 extends
关键字限制泛型(T
, K
等)的取值范围,确保泛型只能是某些特定类型的子集。
ts
function get<T extends string | number>(arg: T): T {
return arg;
}
get("hello"); // ✅ OK
get(42); // ✅ OK
get(true); // ❌ 错误,boolean 不在约束范围内
用法:类型约束 + 类型索引
ts
function getDate<O, K extends keyof O>(user: O, key: K): O[K] {
return user[key]
}
getDate({ name: 'ff', age: 1 }, 'name') // ✅ OK
getDate({ name: 'ld', age: 1 }, 'work') // ❌ 错误,类型"work"的参数不能赋给类型"age"|"name"的参数
4.映射类型
类型映射(Mapped Types) 是一种强大的特性,它允许你基于一个已有的类型,通过遍历其属性并应用变换,生成一个新的类型。
键名重映射(as)
键名重映射(Key Remapping using as
) 是映射类型的一个强大新特性。它允许你在定义映射类型时,动态地修改生成的属性名 ,而不仅仅是继承原类型的键。如:驼峰转蛇形、添加前缀、过滤属性、类型驱动的 API 映射 等高级功能。
用法:所有属性变为只读/解除只读/可选/必选
ini
type Readonly<T> = {
// readonly [P in keyof T]: T[P] // ✅ 所有属性变为**只读**
// -readonly [P in keyof T]: T[P] // ✅ 所有属性变为**非只读**
// [P in keyof T]?: T[P] // ✅ 都变为**可选**属性
// [P in keyof T]-?: T[P] // ✅ 都变为**必选**属性
// [P in keyof T]: T[P] | null // ✅ 所有属性变为**可空**
}
type User = { name: string; age: number }
type ReadonlyUser = Readonly<User>
let data: ReadonlyUser = { name: 'string', age: 22 }
data.age = 2 // ❌ 错误,无法为"age"赋值,因为它是只读属性。
用法 1:深度只读
ts
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
type User = { name: string; age: number; address: { first: string; second: string } }
type DeepReadonlyUser = DeepReadonly<User>
const data: DeepReadonlyUser = { name: 'string', age: 22, address: { f: '北京', s: '海淀' } }
data.address.s = 2 // ❌ 错误,无法为"second"赋值,因为它是只读属性。
用法 2:Pick<T, K>
:从 T 中挑选部分属性
ts
type User = {
id: number
name: string
email: string
}
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type UserNameOnly = Pick<User, 'name'>
用法 3:Record<K, T>
:创建键为 K、值为 T 的对象
ts
type Record<K extends keyof any, T> = {
[P in K]: T
}
// -------例子 1
type Person = Record<string, string>
const xiaoming1: Person = { name: '小明' } // ✅ OK
const xiaoming2: Person = { age: 22 } // ❌ 错误,不能将类型"number"分配给类型"string"。
// -------例子 2
type Keys = 'base' | 'job'
type Info = Record<Keys, { [key: string]: string | number | string[] }>
const data: Info = {
base: {
name: 'rubin',
age: 22
},
job: {
address: '海淀',
list: ['IT', '超越'],
title: false // ❌ 错误,不能将类型"boolean"分配给类型"string | number | string[]"
},
sex: 2222 // ❌ 错误,对象字面量只能指定已知属性,并且"sex"不在类型"Info"中。
}
用法 4:PickFnNames<T, K>
:提取函数名
ts
type PickFnNames<T> = {
[K in keyof T]: T[K] extends typeof Function ? K : never
}
type Actions = {
get: () => string
set: () => void
val: string
}
type Fns = PickFnNames<Actions> // 'get' | 'set'
用法 5:过滤属性,只保留字符串类型
ts
type OnlyString<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K]
}
type User = {
get: () => string
age: number
val: string
}
const data: OnlyString<User> = {
val: 'woshi', // ✅ OK
get: () => { }, // ❌ 错误,对象字面量只能指定已知属性,并且"get"不在类型"OnlyString<User>"中。
age: 2, // ❌ 错误,对象字面量只能指定已知属性,并且"age"不在类型"OnlyString<User>"中。
}
用法 6:排除某些属性(实现 Omit
的新方式)
ts
type Omit<T, P> = {
[K in keyof T as K extends P ? never : K]: T[K]
}
type User = {
name: string
age: number
}
const data: Omit<User, 'name'> = {
name: 'woshi', // ❌ 错误,对象字面量只能指定已知属性,并且"name"不在类型"Omit<User, "name">"中。
age: 22 // ✅ OK
}
工具类型 Exclude Omit Merge Intersection Overwrite
泛型
declear / declear global
declare
用于 声明变量、函数、类、模块或类型 ,但不提供实现。它告诉 TypeScript 编译器:"这个东西在运行时是存在的,你不用管它是怎么来的,请允许我在类型层面使用它。"
它只生成类型信息,不会生成任何 JavaScript 代码。
1. declear 声明一个全局变量/全局函数
ts
// 声明一个全局变量
declare const ENV: string;
declare const VERSION: number;
declare const DEBUG: boolean;
// 声明全局函数(Global Functions)
declare function alert(message: string): void;
declare function $(selector: string): any; // jQuery
console.log(ENV); // ✅ 不报错
2. declare namespace 声明命名空间或模块(.d.ts
文件)
在 .d.ts
类型声明文件中常见:
ts
// utils.d.ts
declare namespace MyUtils {
function capitalize(str: string): string;
function slugify(str: string): string;
}
// 使用
MyUtils.capitalize("hello"); // ✅ 类型检查通过
3. declare global 扩展全局对象(如 window
、NodeJS
)
ts
// 扩展 window 对象
declare global {
interface Window {
myname: string
}
}
Window.myname // ✅ 不报错
typescript
// 扩展 Node.js 的 process
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production';
PORT: string;
}
}
// 使用
process.env.NODE_ENV; // ✅ 类型安全