基础语法
以下是TypeScript的基础语法汇总,涵盖了类型注解、接口、类、函数、泛型等核心概念:
1. 类型注解
TypeScript通过类型注解为变量、函数参数和返回值添加静态类型:
// 基本类型
let num: number = 42;
let str: string = "hello";
let isDone: boolean = false;
let notSure: any = "maybe a string";
// 数组
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b"];
// 元组(固定长度和类型的数组)
let person: [string, number] = ["Alice", 30];
// 枚举
enum Color { Red = 1, Green = 2, Blue = 4 }
let c: Color = Color.Green;
// void 不返回值的函数的返回值
type VoidFunction = () => void;
// object 表示非原始值(即不是 number、string、boolean、null、undefined、symbol 或 bigint 的值)
let obj: object;
obj = {}; // 合法:普通对象
obj = [1, 2, 3]; // 合法:数组
obj = () => {}; // 合法:函数
obj = new Date(); // 合法:类实例
// 错误:不能赋值原始类型
obj = 123; // 错误
obj = "hello"; // 错误
obj = null; // 错误(除非启用 "strictNullChecks": false)
// unknown 表示任何值,必须先判断类型才能使用
function f1(a: any) {
a.b(); // OK
}
function f2(a: unknown) {
a.b();
// 'a' is of type 'unknown'.
}
// never 函数没有返回值
function fail(msg: string): never {
throw new Error(msg);
}
// Function
// 全局类型 Function 描述了 JavaScript 中所有函数值上存在的属性,如 bind、call、apply 和其他属性。它还具有始终可以调用 Function 类型的值的特殊属性;这些调用返回 any
function doSomething(f: Function) {
return f(1, 2, 3);
}
// Rest 参数
// Inferred as 2-length tuple
const args = [8, 5] as const;
const angle = Math.atan2(...args);
2. 接口(Interfaces)
接口定义对象的结构,用于类型检查:
interface Person {
name: string;
age?: number; // 可选属性
readonly id: number; // 只读属性
greet?(msg: string): void; // 可选方法
}
const user: Person = {
name: "Bob",
id: 123,
greet: (msg) => console.log(`${msg}, ${this.name}`)
};
索引访问类型
type Age = Person["age"]; // number
type I1 = Person["age" | "name"]; // number | string
type I2 = Person[keyof Person]; // number | string | (msg: string): void
使用 number 获取数组元素的类型(可以和typeof配合)
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person = typeof MyArray[number]; // type Person = { name: string; age: number;}
index 索引签名,索引签名属性只允许使用某些类型:string、number、symbol、模板字符串模式和仅包含这些类型的联合类型。
interface StringArray {
[index: number]: string; // 此处index为形参,可以任意命名
}
const myArray: StringArray = getStringArray();
const secondItem = myArray[1];
interface NumberOrStringDictionary {
[index: string]: number | string; // index会约束length 和name的类型定义
length: number; // ok, length is a number
name: string; // ok, name is a string
}
extends 接口继承
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
interface ColorfulCircle extends Colorful, Circle {}
const cc: ColorfulCircle = {
color: "red",
radius: 42,
};
&交集
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
type ColorfulCircle = Colorful & Circle;
泛型接口
interface Box<Type> {
contents: Type;
}
interface Apple {
// ....
}
// Same as '{ contents: Apple }'.
type AppleBox = Box<Apple>;
function setContents<Type>(box: Box<Type>, newContents: Type) {
box.contents = newContents;
}
3. 类(Classes)
支持面向对象编程,包括继承、访问修饰符等:
class Point {
// 设置初始值
x = 0;
// 只读属性
readonly name: string = "world";
// 公共成员
public greet() {
console.log("hi!");
}
// protected 成员仅对声明它们的类的子类可见。实例化后是不可见的
protected getName() {
return "hi";
}
// 不允许访问成员,即使从子类也是
private x = 0;
// 静态成员
// 前面可添加public、protected 和 private
// 静态成员也会被继承
static x = 0;
// 特殊静态名称: name、length 和 call属性名无法定义为静态成员
static name = "S!"; // 会报错
}
// private 允许在类型检查期间使用括号表示法进行访问(ts为软私有)
const point = new Point();
console.log(point.x) // 报错, Property 'x' is private and only accessible within class 'Point'.
console.log(point['x']) // 不报错
// JavaScript 的私有字段 (#)为硬私有,不能使用括号表示法进行访问
class Dog {
#barkAmount = 0;
personality = "happy";
constructor() {}
}
class Animal {
protected name: string; // 受保护属性,子类可访问
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
// 继承extends
class Dog extends Animal {
private breed: string; // 私有属性,仅类内部可访问
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
bark() {
console.log("Woof!");
}
}
const dog = new Dog("Buddy", "Labrador");
dog.move(10); // 输出: Buddy moved 10m.
dog.bark(); // 输出: Woof!
// 实现implements
interface Pingable {
ping(): void;
}
// 如果类未能正确实现它,则会发出错误
class Sonar implements Pingable {
ping() {
console.log("ping!");
}
}
// 实现多接口
interface Loggable {
log(message: string): void;
}
interface Serializable {
toJSON(): string;
}
class Logger implements Loggable, Serializable {
log(message: string) {
console.log(message);
}
toJSON() {
return JSON.stringify({ type: "Logger" });
}
}
// 泛型类
class Box<Type> {
// 泛型类的 static 成员永远不能引用类的类型参数
static defaultValue: Type; // 会报错
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
// 基于this的类型守卫
class FileSystemObject {
isFile(): this is FileRep {
return this instanceof FileRep;
}
isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked(): this is Networked & this {
return this.networked;
}
constructor(public path: string, private networked: boolean) {}
}
class FileRep extends FileSystemObject {
constructor(path: string, public content: string) {
super(path, false);
}
}
class Directory extends FileSystemObject {
children: FileSystemObject[];
}
interface Networked {
host: string;
}
const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo");
if (fso.isFile()) {
fso.content; // const fso: FileRep
} else if (fso.isDirectory()) {
fso.children; // const fso: Directory
} else if (fso.isNetworked()) {
fso.host; // const fso: Networked & FileSystemObject
}
// 参数属性:用于将构造函数参数转换为具有相同名称和值的类属性
class Params {
constructor(
public readonly x: number,
protected y: number,
private z: number
) {
// No body necessary
}
}
const a = new Params(1, 2, 3);
console.log(a.x);
(property) Params.x: number
console.log(a.z); // 报错,Property 'z' is private and only accessible within class 'Params'.
// 类表达式,类表达式不需要名称
const someClass = class<Type> {
content: Type;
constructor(value: Type) {
this.content = value;
}
};
const m = new someClass("Hello, world");
// InstanceType 构造函数签名,获取实例化后的类型
class Point {
createdAt: number;
x: number;
y: number
constructor(x: number, y: number) {
this.createdAt = Date.now()
this.x = x;
this.y = y;
}
}
type PointInstance = InstanceType<typeof Point>
function moveRight(point: PointInstance) {
point.x += 5;
}
const point = new Point(3, 4);
moveRight(point);
point.x; // => 8
// abstract类和成员
// 抽象类不能直接实例化
// 抽象方法或抽象字段必须存在于抽象类中
// 如果忘记实现基类的抽象成员,我们将收到一个错误
abstract class Base {
abstract getName(): string;
printName() {
console.log("Hello, " + this.getName());
}
}
// 类结构相同,则两个类的类型相等
// 类之间也存在子类型关系
class Person {
name: string;
age: number;
}
class Employee {
name: string;
age: number;
salary: number;
}
// OK
const p: Person = new Employee();
4. 函数(Functions)
支持类型注解和可选参数:
// 函数类型定义
function add(a: number, b: number): number {
return a + b;
}
// 可选参数(必须放在最后)
function greet(name: string, msg?: string): string {
return msg ? `${msg}, ${name}` : `Hello, ${name}`;
}
// 箭头函数
const multiply = (a: number, b: number): number => a * b;
// 函数调用签名
type DescribableFunction = {
description: string; // 属性签名
(someArg: number): boolean; // 调用签名
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
function myFunc(someArg: number) {
return someArg > 3;
}
myFunc.description = "default description";
doSomething(myFunc);
// 构造签名
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
// 泛型函数
function firstElement<Type>(arr: Type[]): Type | undefined {
return arr[0];
}
// 函数重载
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);
5. 泛型(Generics)
创建可复用的组件,支持多种类型:
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 字面量声明函数
let myIdentity: <Type>(arg: Type) => Type = identity;
// 对象字面量类型的调用签名
let myIdentity: { <Type>(arg: Type): Type } = identity;
let output1 = identity<string>("myString"); // 指定类型
let output2 = identity(100); // 类型推断
// 泛型类
class Box<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
范型约束
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
范型类型为构造函数
// 以下两种声名方式效果一样
// c: { new (): Type } 函数的对象类型声明
// c: new () => A es6箭头函数语法的扩展,此处c只是描述一个构造函数的类型,并非特指箭头函数,和箭头函数不能被new调用并不冲突
// 简单示例
function create<Type>(c: { new (): Type }): Type {
return new c();
}
// 高级示例
class BeeKeeper {
hasMask: boolean = true;
}
class ZooKeeper {
nametag: string = "Mikle";
}
class Animal {
numLegs: number = 4;
}
class Bee extends Animal {
numLegs = 6;
keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;
范型默认值
declare function create<T extends HTMLElement = HTMLDivElement, U extends HTMLElement[] = T[]>(
element?: T,
children?: U
): Container<T, U>;
方差注释
// 协方差(协变): Producer<T> -> Producer<U> 的关系与 T -> U 的关系方向相同
// 逆方差(逆变): Consumer<T> -> Consumer<U> 的关系与 U -> T 的关系相同(方向反转)
// in 逆变
interface Consumer<in T> {
consume: (arg: T) => void;
}
// out 协变
interface Producer<out T> {
make(): T;
}
// in out 不变
interface ProducerConsumer<in out T> {
consume: (arg: T) => void;
make(): T;
}
使用限制:
- in T(逆变):T 只能出现在输入位置(如参数)。
- out T(协变):T 只能出现在输出位置(如返回值)。
- in out T(不变):T 可同时出现在输入和输出位置,但类型必须严格相等。
6. 联合类型(Union Types)
允许变量具有多种类型之一:
let value: string | number;
value = "hello"; // 合法
value = 100; // 合法
// value = true; // 错误:不是 string 或 number
7. 类型别名(Type Aliases)
为类型创建自定义名称:
type Point = {
x: number;
y: number;
};
type ID = string | number;
function printCoord(pt: Point) {
console.log(`(${pt.x}, ${pt.y})`);
}
8. 交叉类型(Intersection Types)
组合多个类型为一个类型:
type Admin = { role: string };
type User = { name: string };
type AdminUser = Admin & User; // 同时拥有 role 和 name
const adminUser: AdminUser = {
role: "admin",
name: "Alice"
};
9. 类型操作
typeof
在运行时检查类型:
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // 类型缩小为 string
} else {
console.log(value.toFixed(2)); // 类型缩小为 number
}
}
in运算符
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
is类型谓词
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
!非 null 断言
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius! ** 2;
}
}
never穷举性检查
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
extends 类型约束
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
// Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
条件类型
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example1 = Dog extends Animal ? number : string; // number
// 条件类型约束
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
type EmailMessageContents = MessageOf<Email>; // type EmailMessageContents = string
type DogMessageContents = MessageOf<Dog>; // type DogMessageContents = never
分布式条件类型
type ToArray<Type> = Type extends any ? Type[] : never;
// 分布在string | number
type StrArrOrNumArr = ToArray<string | number>; // type StrArrOrNumArr = string[] | number[]
// 若不想被分布,可以用方括号将 extends 关键字的每一侧括起来
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
// 'ArrOfStrOrNum' is no longer a union.
type ArrOfStrOrNum = ToArrayNonDist<string | number>; // type ArrOfStrOrNum = (string | number)[]
类型映射
映射类型建立在索引签名的语法之上,通常通过 keyof 创建来迭代键以创建类型
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
type CreateMutable<Type> = {
readonly [Property in keyof Type]: Type[Property];
};
as 子句类型重映射
// Remove the 'kind' property
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>; // type KindlessCircle = { radius: number;}
模板文本类型
// 联合类型会交叉相乘
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
// 根据类型内部的信息定义新字符串
const person = makeWatchedObject({
firstName: "Saoirse",
lastName: "Ronan",
age: 26,
});
// makeWatchedObject has added `on` to the anonymous Object
person.on("firstNameChanged", (newValue) => {
console.log(`firstName was changed to ${newValue}!`);
});
//
type PropEventSource<Type> = {
on<Key extends string & keyof Type>
(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void;
};
/// Create a "watched object" with an `on` method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
// 内置字符串操作类型:
// Uppercase<StringType> 转大写
type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting> // type ShoutyGreeting = "HELLO, WORLD"
// Lowercase<StringType> 转小写
// Capitalize<StringType> 首字母转大写
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"
// Uncapitalize<StringType> 首字母转小写
10. 枚举(Enums)
为一组数值或字符串赋予友好名称:
// 数字枚举(默认从0开始)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 字符串枚举
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
// 枚举与数字兼容,数字与枚举兼容。来自不同枚举类型的枚举值被视为不兼容
enum Status {
Ready,
Waiting,
}
enum Color {
Red,
Blue,
Green,
}
let status = Status.Ready;
status = Color.Green; // Error
11. 装饰器(Decorators)
用于类和类成员的元编程:
// 类装饰器示例
function logClass(constructor: Function) {
console.log(`Class ${constructor.name} was created`);
}
@logClass
class MyClass {
// ...
}
12. 命名空间(Namespaces)
组织代码的方式,避免全局命名冲突:
namespace Validation {
export interface Validator {
isValid(value: any): boolean;
}
export class EmailValidator implements Validator {
isValid(email: string) {
return email.includes("@");
}
}
}
const validator: Validation.Validator = new Validation.EmailValidator();
13. 类型断言(Type Assertion)
手动指定变量类型:
let value: any = "hello";
let length: number = (value as string).length; // 或 <string>value
14. 可选链(Optional Chaining)
安全访问可能不存在的属性:
const user = {
name: "Alice",
address: {
street: "123 Main St"
}
};
const street = user.address?.street; // 如果 address 不存在,返回 undefined
15. 空值合并(Nullish Coalescing)
仅当左侧值为 null 或 undefined 时使用右侧默认值:
const count = null ?? 100; // 结果:100
const name = "" ?? "Default"; // 结果:""(空字符串不为 null/undefined)
16.类型运算符keyof
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; // A = number
type A = number
type Mapish = { [k: string]: boolean };
type M = keyof Mapish; // M = number | string, JavaScript 对象键总是被强制转换为字符串
17.类型推断infer
使用限制:
-
只能在条件类型中使用:
infer必须出现在extends子句中,例如:// 正确
type A<T> = T extends infer U ? U : never;
// 错误:不能直接使用infer
type B<T> = infer U; // 报错 -
推断变量必须在条件类型的true分支中使用:
// 正确
type C<T> = T extends infer U ? U : never;
// 错误:infer U未被使用
type D<T> = T extends infer U ? never : never; // 无意义 -
多条件推断中的类型合并:
当infer出现在联合类型中时,会生成所有可能的推断结果的联合:type UnionReturnType<T> = T extends () => infer R ? R : never;
type Result = UnionReturnType<(() => number) | (() => string)>; // 结果:number | string
提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
type Num = ReturnType<() => number>; // 结果:number
type Str = ReturnType<(x: string) => string>; // 结果:string
- 解释:
infer R捕获函数的返回类型,若T是函数类型,则返回R,否则返回never。
提取数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : T;
// 使用示例
type Num = ElementType<number[]>; // 结果:number
type Str = ElementType<string[]>; // 结果:string
type NotArray = ElementType<boolean>; // 结果:boolean
- 解释:
若T是数组类型,则infer U捕获其元素类型;否则返回T本身。
提取Promise的resolved类型
type ResolvedType<T> = T extends Promise<infer U> ? U : T;
// 使用示例
type Num = ResolvedType<Promise<number>>; // 结果:number
type Str = ResolvedType<Promise<string>>; // 结果:string
type NotPromise = ResolvedType<boolean>; // 结果:boolean
- 解释:
若T是Promise类型,则infer U捕获其内部的resolved类型;否则返回T。
提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 使用示例
type Args = Parameters<(a: number, b: string) => void>; // 结果:[number, string]
- 解释:
infer P捕获函数的参数类型,返回一个元组类型。
提取构造函数的实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;
// 使用示例
class MyClass { constructor() {} }
type MyClassInstance = InstanceType<typeof MyClass>; // 结果:MyClass
- 解释:
infer R捕获构造函数的返回类型(即实例类型)。
条件类型链中的多重推断
type DeepReturnType<T> =
T extends () => infer U ?
U extends () => any ? DeepReturnType<U> : U :
T;
// 使用示例
type NestedFn = () => () => () => number;
type Result = DeepReturnType<NestedFn>; // 结果:number
- 解释:
递归推断嵌套函数的最终返回类型。
与泛型结合
type GetReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
function add(a: number, b: number): number {
return a + b;
}
type AddResult = GetReturnType<typeof add>; // 结果:number
与映射类型结合
type PromiseValues<T> = {
[K in keyof T]: T[K] extends Promise<infer U> ? U : T[K];
};
// 使用示例
type Obj = {
a: Promise<number>;
b: string;
};
type Result = PromiseValues<Obj>; // 结果:{ a: number; b: string; }
18.模块
// 只能导入类型的 import 语句:
import type { Cat, Dog } from "./animal.js";
// 单个导入类型,以 type 为前缀
import { createCatName, type Cat, type Dog } from "./animal.js";