01-基础类型与接口
分类:04-TypeScript类型系统
发布于:
阅读时间:56 分钟
基础类型与接口
📋 学习目标
- 掌握TypeScript的基本数据类型
- 理解接口的定义和使用方法
- 学会类型别名和类型断言的应用
- 掌握联合类型和交叉类型的使用
🎯 环境配置
1. 安装和配置TypeScript
# 全局安装TypeScript
npm install typescript -g
# 项目安装
npm install typescript --save-dev
# 安装类型声明文件
npm install @types/node --save-dev
# 安装ts-node用于直接运行TS文件
npm install ts-node -g
2. 基本编译命令
# 编译TS文件
tsc filename.ts
# 编译并自动运行
ts-node filename.ts
# 监听模式自动编译
tsc --watch filename.ts
🔧 基础数据类型
1. 原始类型
// Boolean类型
let isDone: boolean = false;
let isVisible: boolean = true;
// Number类型
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let float: number = 3.14;
// String类型
let color: string = "blue";
let templateString: string = `This is a template string with ${color}`;
// 注意:Boolean构造函数创建的是对象,不是布尔值
// ❌ 错误
let createdByBoolean: boolean = new Boolean(1); // Type 'Boolean' is not assignable to type 'boolean'
// ✅ 正确
let createdByBoolean: boolean = Boolean(1); // true
let isTrue: boolean = true;
2. 特殊类型
// Undefined类型
let u: undefined = undefined;
// Null类型
let n: null = null;
// Void类型 - 表示没有返回值
function warnUser(): void {
console.log("This is a warning message");
}
// Never类型 - 表示永不存在的值的类型
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
// Any类型 - 任意类型(尽量避免使用)
let notSure: any = 4;
notSure = "maybe a string";
notSure = false;
// Unknown类型 - 类型安全的any
let value: unknown = "hello world";
// ❌ 错误:unknown类型不能直接操作
// let upperCaseValue: string = value.toUpperCase();
// ✅ 正确:需要类型检查
if (typeof value === "string") {
let upperCaseValue: string = value.toUpperCase();
}
3. 对象类型
// Array类型
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
let list3: string[] = ["a", "b", "c"];
let list4: any[] = [1, "two", true];
// 元组类型 - 固定长度和类型的数组
let tuple1: [string, number] = ["hello", 10];
tuple1[0] = "world";
// tuple1[0] = 10; // ❌ 错误:Type 'number' is not assignable to type 'string'
// 可选元组元素
let tuple2: [number, string?, boolean?] = [1];
let tuple3: [number, string?, boolean?] = [1, "hello"];
let tuple4: [number, string?, boolean?] = [1, "hello", true];
// 只读元组
let readonlyTuple: readonly [number, string] = [1, "hello"];
// readonlyTuple[0] = 2; // ❌ 错误:Cannot assign to '0' because it is a read-only property
// Object类型
let obj: object = {prop: 0};
let obj2: { name: string; age: number } = {
name: "张三",
age: 25
};
// Function类型
let myAdd: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y;
};
// 函数声明
function add(x: number, y: number): number {
return x + y;
}
// 函数表达式
const subtract = function(x: number, y: number): number {
return x - y;
};
// 箭头函数
const multiply = (x: number, y: number): number => x * y;
🎯 接口(Interface)
1. 基本接口定义
// 基本接口
interface Person {
name: string;
age: number;
}
// 使用接口
let user: Person = {
name: "张三",
age: 25
};
// ❌ 错误:缺少必需属性
// let user2: Person = {
// name: "李四"
// };
// ❌ 错误:属性类型不匹配
// let user3: Person = {
// name: "王五",
// age: "25" // Type 'string' is not assignable to type 'number'
// };
2. 可选属性
interface User {
id: number;
name: string;
age?: number; // 可选属性
email?: string; // 可选属性
}
let user1: User = {
id: 1,
name: "张三"
};
let user2: User = {
id: 2,
name: "李四",
age: 30,
email: "lisi@example.com"
};
3. 只读属性
interface Config {
readonly id: number;
readonly apiUrl: string;
timeout: number;
}
let config: Config = {
id: 1,
apiUrl: "https://api.example.com",
timeout: 5000
};
// ❌ 错误:Cannot assign to 'id' because it is a read-only property
// config.id = 2;
// ✅ 正确:可以修改非只读属性
config.timeout = 3000;
4. 任意属性
interface FlexiblePerson {
name: string;
age?: number;
[propName: string]: any; // 允许任意属性
}
let person: FlexiblePerson = {
name: "张三",
gender: "male", // 额外属性
city: "北京", // 额外属性
hobby: ["coding", "reading"]
};
// ⚠️ 注意:当有任意属性时,其他属性类型必须兼容
interface Problematic {
name: string;
[propName: string]: number; // ❌ 错误:string类型不能赋值给number
}
// ✅ 正确:使用联合类型
interface Fixed {
name: string;
[propName: string]: string | number;
}
5. 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string): boolean {
return source.search(subString) > -1;
};
// 简化的函数类型
let mySearch2: (source: string, subString: string) => boolean = function(source, subString) {
return source.search(subString) > -1;
};
6. 接口继承
// 单继承
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
let myDog: Dog = {
name: "旺财",
breed: "金毛"
};
// 多继承
interface Shape {
color: string;
}
interface Square {
sideLength: number;
}
interface ColoredSquare extends Shape, Square {
borderColor: string;
}
let mySquare: ColoredSquare = {
color: "red",
sideLength: 10,
borderColor: "black"
};
7. 混合类型接口
interface Counter {
(start: number): string; // 函数
interval: number; // 属性
reset(): void; // 方法
}
function getCounter(): Counter {
let counter = (function(start: number) {
return start.toString();
}) as Counter;
counter.interval = 123;
counter.reset = function() {
console.log("Reset called");
};
return counter;
}
let c = getCounter();
console.log(c(10));
c.reset();
c.interval = 5.0;
🔧 类型别名(Type Alias)
1. 基本类型别名
// 简单类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
} else {
return n();
}
}
// 对象类型别名
type Person = {
name: string;
age: number;
};
// 函数类型别名
type AddFunction = (a: number, b: number) => number;
const add: AddFunction = (a, b) => a + b;
2. 泛型类型别名
type Container<T> = { value: T };
type Tree<T> = {
value: T;
left?: Tree<T>;
right?: Tree<T>;
};
// 使用
let numberContainer: Container<number> = { value: 42 };
let stringTree: Tree<string> = {
value: "root",
left: { value: "left" },
right: { value: "right" }
};
3. 联合类型与交叉类型
// 联合类型(Union Types)
type Status = 'pending' | 'success' | 'error';
type ID = string | number;
function printId(id: ID) {
console.log(`ID: ${id}`);
}
printId(101); // ID: 101
printId("ABC123"); // ID: ABC123
// 交叉类型(Intersection Types)
interface BusinessPartner {
name: string;
credit: number;
}
interface Identity {
id: number;
email: string;
}
type Employee = BusinessPartner & Identity;
// 使用
let emp: Employee = {
name: "John Doe",
credit: 7000,
id: 100,
email: "john.doe@example.com"
};
🎯 类型断言(Type Assertion)
1. 基本类型断言
// 尖括号语法(JSX中不可用)
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as语法(推荐)
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;
2. 联合类型断言
interface Cat {
type: 'cat';
purr(): void;
}
interface Dog {
type: 'dog';
bark(): void;
}
type Animal = Cat | Dog;
function makeSound(animal: Animal) {
if ((animal as Cat).type === 'cat') {
(animal as Cat).purr();
} else {
(animal as Dog).bark();
}
}
3. 非空断言
// 可能是null或undefined的值
let value: string | null = "hello";
// ❌ 错误:Object is possibly 'null'
// console.log(value.length);
// ✅ 使用非空断言
console.log(value!.length);
// 在函数中使用
function processValue(value: string | undefined) {
// 非空断言告诉TypeScript value不是undefined
const processedValue = value!.toUpperCase();
return processedValue;
}
4. const断言
// 普通数组
let array1 = [1, 2, 3];
array1.push(4); // 可以修改
// const断言 - 创建只读字面量类型
let array2 = [1, 2, 3] as const;
// array2.push(4); // ❌ 错误:Property 'push' does not exist on type 'readonly [1, 2, 3]'
// 对象const断言
let obj1 = { x: 10, y: 20 };
obj1.x = 30; // 可以修改
let obj2 = { x: 10, y: 20 } as const;
// obj2.x = 30; // ❌ 错误:Cannot assign to 'x' because it is a read-only property
// 字面量类型推断
let direction = "north"; // 类型是string
let direction2 = "north" as const; // 类型是"north"
🚀 高级类型技巧
1. 条件类型
// 基本条件类型
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
// 条件类型与映射
type NonNullable<T> = T extends null | undefined ? never : T;
type Test3 = NonNullable<string | null>; // string
type Test4 = NonNullable<number | undefined>; // number
2. 映射类型
// 基本映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 使用
interface User {
name: string;
age: number;
email: string;
}
type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
// 自定义映射类型
type Stringify<T> = {
[P in keyof T]: string;
};
type StringifiedUser = Stringify<User>;
// { name: string; age: string; email: string; }
3. 索引类型
// 索引访问类型
interface Person {
name: string;
age: number;
address: string;
}
type PersonName = Person['name']; // string
type PersonKeys = keyof Person; // "name" | "age" | "address"
// 泛型索引类型
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let person: Person = {
name: "张三",
age: 25,
address: "北京"
};
let name = getProperty(person, "name"); // string
// let age = getProperty(person, "salary"); // ❌ 错误:Argument of type '"salary"' is not assignable to parameter of type '"name" | "age" | "address"'
⚠️ 常见陷阱
1. 类型推断陷阱
// ❌ 错误的类型推断
let a = []; // 推断为 any[]
a.push(1);
a.push("string");
// ✅ 明确指定类型
let b: number[] = [];
// 或者
let c = [] as number[];
2. 接口与类型别名的选择
// 接口:支持声明合并
interface User {
name: string;
}
interface User {
age: number;
}
// 合并后的接口:{ name: string; age: number; }
// 类型别名:不支持声明合并,但支持更灵活的表达
type ID = string | number;
type User2 = {
[K in keyof User]: User[K];
} & { id: ID };
3. any类型的滥用
// ❌ 滥用any
function processData(data: any): any {
return data.map((item: any) => item.id);
}
// ✅ 使用泛型
function processData<T extends { id: any }>(data: T[]): T[] {
return data.filter(item => item.id);
}
📝 最佳实践
- 优先使用interface:定义对象类型时
- 合理使用type:需要联合类型、交叉类型或复杂类型表达式时
- 避免使用any:尽量使用unknown或具体类型
- 使用const断言:创建不可变的字面量类型
- 利用类型推断:让TypeScript自动推断类型,减少冗余的类型注解
🎯 小结
- 掌握了TypeScript的基本数据类型
- 理解了接口的定义和使用方法
- 学会了类型别名和类型断言的应用
- 掌握了联合类型和交叉类型的使用
- 了解了高级类型技巧和最佳实践
下一步学习:泛型编程