正文
目录会跟随阅读位置移动。
阅读进度

| JS 写法 | TS 写法 |
|---|---|
let a = 10 |
let a: number = 10 |
let b = 'hi' |
let b: string = 'hi' |
let c = true |
let c: boolean = true |
let d = null |
let d: null = null |
let e = undefined |
let e: undefined = undefined |
let f = [1,2,3] |
let f: number[] = [1,2,3] 或 Array<number> |
let g = {name:'Tom'} |
let g: {name: string} = {name:'Tom'} |
TS 新增类型:
any:关闭类型检查(尽量少用)unknown:安全的任意值(使用前需类型断言)void:无返回值never:永远不会发生(如抛出错误)tuple:固定长度数组,如 [string, number]// 自动推断
let age = 25; // 等价于 let age: number = 25
// 需要显式注解的情况
let user: { name: string; age?: number };
let callback: (x: number) => void;
// 参数和返回值都要加类型
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数
const add = (a: number, b: number): number => a + b;
// 可选参数
function greet(name: string, age?: number): string {
return age ? `${name} is ${age}` : name;
}
// 默认参数
function multiply(a: number, b: number = 1): number {
return a * b;
}
interface User {
name: string;
age: number;
email?: string; // 可选属性
readonly id: number; // 只读属性
}
const tom: User = {
name: 'Tom',
age: 18,
id: 1
};
接口 vs 类型别名(type):
联合类型 是 TypeScript 中一种强大的类型系统特性,允许一个变量同时拥有多种可能的类型,用竖线 | 分隔。
// 变量可以是 string 或 number
let id: string | number;
id = 'abc123'; // 合法
id = 456; // 合法
id = true; // 错误,boolean 不在联合类型中
// 函数参数可以是多种类型
function format(value: string | number): string {
return value.toString();
}
联合类型的问题与解决
问题:只能访问所有类型的共同成员
let id: string | number = 'abc';
// 错误:number 没有 toUpperCase 方法
console.log(id.toUpperCase());
// 错误:string 没有 toFixed 方法
console.log(id.toFixed(2));
// 可以访问共同成员(所有类型都有 toString)
console.log(id.toString()); // string 和 number 都有 toString
解决方案:类型守卫
类型守卫是 TypeScript 中用来在运行时检查类型的一种机制,它让 TS 能够在某个作用域内智能地缩小类型的范围。
为什么需要类型守卫?
当你使用联合类型时,TS 不确定具体是哪种类型,很多操作无法直接进行。
function printId(id: string | number) {
console.log(id.toUpperCase()); // 错误:number 没有 toUpperCase 方法
console.log(id.toFixed(2)); // 错误:string 没有 toFixed 方法
}
function printId(id: string | number) {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // TS 知道这里是 string
} else {
console.log(id.toFixed(2)); // TS 知道这里是 number
}
}
class Dog { bark() { console.log('汪汪'); } }
class Cat { meow() { console.log('喵喵'); } }
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // TS 知道这里是 Dog
} else {
animal.meow(); // TS 知道这里是 Cat
}
}
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim(); // TS 知道这里有 swim 方法
} else {
animal.fly(); // TS 知道这里有 fly 方法
}
}
interface Cat {
name: string;
meow(): void;
}
function isCat(animal: any): animal is Cat {
return animal && typeof animal.meow === 'function';
}
function handle(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // TS 知道这里是 Cat
}
}
// 数组
let list: number[] = [1, 2, 3];
let list2: Array<string> = ['a', 'b'];
// 元组(固定长度,每个位置类型固定)
let point: [number, number] = [10, 20];
let httpResponse: [number, string] = [200, 'OK'];
当你比TS更了解某个值的类型时使用
let someValue: unknown = 'hello world';
// 方式1:as 语法(推荐)
let strLength: number = (someValue as string).length;
// 方式2:尖括号语法(JSX 中不可用)
let strLength2: number = (<string>someValue).length;
interface Person {
name: string;
age: number;
email: string;
}
type WithoutEmail = Omit<Person, 'email'>; // 排除 email
type PartialPerson = Partial<Person>; // 全部变为可选
type ReadonlyPerson = Readonly<Person>; // 全部变为只读
type NameAndAge = Pick<Person, 'name' | 'age'>; // 只选 name 和 age
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true, // 开启全部严格检查
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src"
}
}