logo
火山博客
导航

TypeScript 5.4 新特性:NoInfer 类型 - 控制类型推断的新方式

2024-11-19
12阅读时间3分钟

1. 为什么需要 NoInfer?

在 TypeScript 中,类型推断有时会过于灵活,导致一些意外的行为:

Typescript
// 问题示例
function merge<T>(a: T, b: T) {
    return { ...a, ...b };
}

// TypeScript 会推断 T 为 number | string
merge(123, "hello"); // 这样是允许的,但可能不是我们想要的

2. NoInfer 的工作原理

Typescript
// NoInfer 的实现
type NoInfer<T> = [T][T extends any ? 0 : never];

// 使用 NoInfer 改进
function merge<T>(a: T, b: NoInfer<T>) {
    return { ...a, ...b };
}

merge(123, "hello"); // ❌ 错误:类型 'string' 不能赋值给类型 'number'
merge(123, 456);     // ✅ 正确

3. 常见使用场景

1 函数参数类型控制

Typescript
// 确保回调函数的参数类型与原始数据一致
function transform<T>(
    data: T[],
    callback: (item: NoInfer<T>) => void
) {
    data.forEach(callback);
}

const numbers = [1, 2, 3];
transform(numbers, (n: string) => {}); // ❌ 错误
transform(numbers, (n: number) => {}); // ✅ 正确

2 泛型约束中的应用

Typescript
interface Comparable<T> {
    compareTo(other: NoInfer<T>): number;
}

class NumberComparable implements Comparable<number> {
    value: number;
    constructor(value: number) {
        this.value = value;
    }
    
    compareTo(other: number): number {
        return this.value - other;
    }
}

3 API 设计中的应用

Typescript
interface CacheConfig<T> {
    initialData: T;
    validator: (data: NoInfer<T>) => boolean;
    serializer?: (data: NoInfer<T>) => string;
}

function createCache<T>(config: CacheConfig<T>) {
    // 实现细节
}

// 使用示例
createCache({
    initialData: { id: 1, name: "test" },
    validator: (data: string) => true  // ❌ 错误:类型不匹配
});

4. 与其他工具类型的组合

Typescript
type User = {
    id: number;
    name: string;
};

function updateUser<T extends keyof User>(
    field: T,
    value: NoInfer<User[T]>
) {
    // ...
}

updateUser("id", "123");    // ❌ 错误:期望 number 类型
updateUser("name", "John"); // ✅ 正确

5. 最佳实践

1. 类型安全:使用 NoInfer 在需要严格类型匹配的场景

Typescript
function assertEqual<T>(
    actual: T,
    expected: NoInfer<T>,
    message?: string
) {
    if (actual !== expected) {
        throw new Error(message || `Expected ${expected} but got ${actual}`);
    }
}

2. API 设计:在公共 API 中使用 NoInfer 提供更好的类型提示

Typescript
interface ValidationRule<T> {
    validate: (value: T) => boolean;
    message: (value: NoInfer<T>) => string;
}

3. 泛型约束:与泛型约束结合使用

Typescript
function clamp<T extends number>(
    value: T,
    min: NoInfer<T>,
    max: NoInfer<T>
): T {
    return Math.min(Math.max(value, min), max) as T;
}

6. 注意事项

  • 不要过度使用 NoInfer,只在真正需要控制类型推断的地方使用
  • 考虑是否可以通过其他方式(如显式类型注解)达到同样的效果
  • 在使用 NoInfer 时,确保提供良好的错误信息和类型提示
2024 © Powered by
hsBlog
|
后台管理