内置数据类型与工具类型
2026/7/9大约 6 分钟
内置数据类型与工具类型
Go 开发者已知
在 Go 中,类型系统在编译期是严格的,但运行时可以通过 reflect 包来检查和操作任意类型。Go 的基础类型包括:
// Go 基础类型
var i int // int, int8, int16, int32, int64
var u uint // uint, uint8, uint16, uint32, uint64
var f float64 // float32, float64
var b bool // bool
var s string // string
var by byte // byte (uint8 别名)
var r rune // rune (int32 别名,代表 Unicode 码点)
// 复合类型
var arr [3]int // 数组(定长)
var sli []int // 切片(变长)
var m map[string]int // 映射
var st struct{ Name string } // 结构体
var fn func(int) bool // 函数
var iface interface{} // 接口(空接口)Go 的 reflect 包可以在运行时获取类型信息和值操作:
import "reflect"
func Inspect(v any) {
t := reflect.TypeOf(v) // 获取静态类型信息
vv := reflect.ValueOf(v) // 获取运行时值信息
fmt.Println(t.Kind()) // reflect.Int, reflect.String 等
fmt.Println(t.Name()) // 类型名称
fmt.Println(vv.Kind()) // 值的种类
// 遍历结构体字段
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("Field: %s, Type: %s\n", field.Name, field.Type)
}
}
}TS 怎么做
TypeScript 的类型系统是结构类型系统(Structural Type System),且类型仅在编译期存在,运行时会被擦除。JS 的运行时类型信息非常有限。
typeof 操作符
// TS 中的 typeof —— 编译期与运行时含义不同
const str = "hello";
type T = typeof str; // 编译期: type T = string
// 运行时 typeof —— 局限很大
console.log(typeof "hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" —— 著名的 JS 历史遗留 bug
console.log(typeof []); // "object" —— 无法区分数组与普通对象
console.log(typeof {}); // "object"
console.log(typeof new Date()); // "object"
console.log(typeof (() => {})); // "function"typeof 的局限
typeof null === "object" 是 JavaScript 自诞生以来的一个 bug,但由于已有大量代码依赖此行为,至今无法修复。同时,typeof 无法区分数组、普通对象、Date 等——它们都返回 "object"。
Object.keys / values / entries
// 对象反射操作
interface User {
id: number;
name: string;
email: string;
}
const user: User = { id: 1, name: "Alice", email: "alice@example.com" };
// 获取键数组(注意:返回 string[] 而非 (keyof User)[])
const keys = Object.keys(user);
// ^? string[]
// 获取值数组
const values = Object.values(user);
// ^? unknown[]
// 获取键值对数组
const entries = Object.entries(user);
// ^? [string, unknown][]类型安全缺陷
Object.keys() 返回 string[] 而非 (keyof T)[],这是 TS 团队的有意设计——因为对象在运行时可能包含原型链上继承的额外属性。如果确信对象纯净,可以自己封装:
function typedKeys<T extends object>(obj: T): (keyof T)[] {
return Object.keys(obj) as (keyof T)[];
}Object.assign / freeze
// Object.assign —— 合并对象
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const merged = Object.assign(target, source);
// target 被原地修改: { a: 1, b: 3, c: 4 }
// merged === target (引用相同)
// 推荐用展开运算符替代
const merged2 = { ...target, ...source };
// Object.freeze —— 冻结对象(浅冻结)
const frozen = Object.freeze({ x: 1, y: { z: 2 } });
frozen.x = 42; // 严格模式下报错
frozen.y.z = 99; // 可以修改!嵌套对象未冻结内置工具类型 —— 分类详解
TypeScript 内置了大量工具类型,按功能分类如下:
1. 属性修饰工具类型
// Partial<T> —— 所有属性变为可选
interface Config { url: string; port: number; timeout: number; }
type PartialConfig = Partial<Config>;
// { url?: string; port?: number; timeout?: number; }
// Required<T> —— 所有属性变为必选
type RequiredConfig = Required<PartialConfig>;
// Readonly<T> —— 所有属性变为只读
type ReadonlyConfig = Readonly<Config>;
// 实现原理
type MyPartial<T> = { [K in keyof T]?: T[K] };
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };2. 联合类型工具类型
// Exclude<T, U> —— 从 T 中排除 U 中的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<string | number | boolean, string | number>; // boolean
// Extract<T, U> —— 提取 T 中属于 U 的类型
type T2 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> —— 排除 null 和 undefined
type T3 = NonNullable<string | null | undefined>; // string
// Record<K, T> —— 构造对象类型
type PageInfo = Record<string, { title: string; url: string }>;3. 函数与返回值工具类型
// ReturnType<T> —— 获取函数返回值类型
function greet(name: string): string { return `Hello ${name}`; }
type GreetReturn = ReturnType<typeof greet>; // string
// Parameters<T> —— 获取函数参数类型元组
type GreetParams = Parameters<typeof greet>; // [name: string]
// ConstructorParameters<T> —— 获取构造函数参数类型
class Foo { constructor(a: number, b: string) {} }
type FooParams = ConstructorParameters<typeof Foo>; // [a: number, b: string]
// InstanceType<T> —— 获取实例类型
type FooInstance = InstanceType<typeof Foo>;4. 字符串字面量工具类型(TS 4.1+)
// Uppercase/Lowercase/Capitalize/Uncapitalize
type Upper = Uppercase<"hello">; // "HELLO"
type Lower = Lowercase<"HELLO">; // "hello"
type Cap = Capitalize<"hello">; // "Hello"
type Uncap = Uncapitalize<"Hello">; // "hello"
// 实用场景:统一 API 响应中的键名
type ApiResponse = {
user_name: string;
user_email: string;
};
type CamelCaseKeys = {
[K in keyof ApiResponse as CamelCase<K & string>]: ApiResponse[K];
};
// 需自行实现 CamelCase 工具类型5. Pick / Omit
// Pick<T, K> —— 选取属性
interface Person { name: string; age: number; email: string; }
type NameAndAge = Pick<Person, "name" | "age">;
// { name: string; age: number; }
// Omit<T, K> —— 排除属性
type WithoutEmail = Omit<Person, "email">;
// { name: string; age: number; }
// 实现原理
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;差异分析
| 维度 | Go | TypeScript |
|---|---|---|
| 类型存在时间 | 编译期 + 运行时 | 仅编译期,运行时擦除 |
| 运行时类型检查 | reflect.TypeOf / reflect.ValueOf | typeof(局限)+ instanceof(有限) |
| 类型反射能力 | 完整 — 字段遍历、方法调用、值修改 | 无反射能力,需借助 TS 编译器 API |
| 结构体字段操作 | reflect.StructField 完整信息 | Object.keys() 返回 string[] |
| 对象冻结 | 无内置,需用 sync/atomic 或自定义 | Object.freeze() 内置(浅冻结) |
| 对象合并 | 手写循环或使用第三方库 | Object.assign() / 展开运算符 |
Bad Practice
1. 滥用 as 断言绕过类型系统
// Bad: 用 as any 绕过类型检查
const data = JSON.parse(response) as any;
console.log(data.user.name.toUpperCase()); // 运行时可能崩溃
// 也不要用双重断言
const num = (42 as unknown) as string; // 完全错误的类型2. 过度依赖 typeof 进行运行时判断
// Bad: typeof 无法区分复杂类型
function process(value: unknown) {
if (typeof value === "object") {
// 这里 value 可能是: null、数组、普通对象、Date...
value.foo; // TS 报错:Object is possibly null
}
}3. 修改 frozen 对象而不检查
// Bad: 尝试修改 frozen 对象
const settings = Object.freeze({ theme: "dark", lang: "zh" });
settings.theme = "light"; // 静默失败(非严格模式)或报错(严格模式)Best Practice
1. 自定义类型安全的对象操作函数
// Best: 类型安全的 keys 封装
function keys<T extends Record<string, unknown>>(obj: T): (keyof T)[] {
return Object.keys(obj) as (keyof T)[];
}
// 类型安全的 entries
function entries<T extends Record<string, unknown>>(obj: T): [keyof T, T[keyof T]][] {
return Object.entries(obj) as [keyof T, T[keyof T]][];
}
interface User {
id: number;
name: string;
}
const user: User = { id: 1, name: "Alice" };
for (const key of keys(user)) {
console.log(key); // key 类型为 "id" | "name"
}2. 深度冻结工具函数
// Best: 深度冻结
function deepFreeze<T extends Record<string, any>>(obj: T): Readonly<T> {
const propNames = Object.getOwnPropertyNames(obj);
for (const name of propNames) {
const value = obj[name];
if (value && typeof value === "object") {
deepFreeze(value);
}
}
return Object.freeze(obj);
}
const config = deepFreeze({ nested: { value: 42 } });
config.nested.value = 0; // 严格模式下报错3. 使用类型守卫替代 typeof
// Best: 使用类型守卫
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function process(value: unknown) {
if (isRecord(value)) {
value.foo; // 类型安全
}
}4. 工具类型组合使用
// Best: 组合工具类型表达精确约束
interface ServerConfig {
host: string;
port: number;
tls: boolean;
cert?: string;
key?: string;
}
// 只读且必填的配置类型(排除可选属性)
type StrictServerConfig = Readonly<Required<Pick<ServerConfig, "host" | "port" | "tls">>>;核心建议
TypeScript 的工具类型是编译期类型转换的强大工具,配合类型守卫、泛型约束可以编写高度类型安全的代码。记住:TS 类型是编译期幻影,运行时你需要 zod、io-ts 等方案做真正的数据校验(详见第8篇)。