代码之家  ›  专栏  ›  技术社区  ›  aleclarson

通过“is”运算符和条件类型收缩的泛型类型

  •  3
  • aleclarson  · 技术社区  · 6 年前

    我想用 is 运算符将泛型类型缩小为“基元类型的联合”或 object 类型。任何一种方法都适用于我的用例。

    以下代码片段的正确解决方法是什么?( playground )

    class Foo<T = any> {
        constructor(value: (() => T) | (T extends Function ? never : T)) {}
    }
    
    // Function values must be returned by a getter:
    new Foo(() => () => {}) // OK: Inferred as "Foo<() => void>(value: () => () => void)"
    
    // All other values can be optionally wrapped with a getter, or passed as-is:
    new Foo(() => 1) // OK: Inferred as "Foo<1>(value: 1 | (() => 1))"
    new Foo(1)       // OK: Inferred as "Foo<1>(value: 1 | (() => 1))"
    
    // Try narrowing to a primitive type:
    function testPrimitive<T>(value: T) {
        if (isPrimitive(value)) {
            return new Foo(value) // ERROR: "Type 'T & string' is not assignable to type 'T & symbol extends Function ? never : T & symbol'."
        }
    }
    
    // Try narrowing to an object type:
    function testObject<T>(value: T) {
        if (isObject(value)) {
            return new Foo(value) // ERROR: "Type 'T & object' is not assignable to type 'T & object extends Function ? never : T & object'."
        }
    }
    
    type Primitive = string | number | boolean | symbol | null | undefined
    
    const isPrimitive = (value: any): value is Primitive =>
        !value || typeof value !== 'object'
    
    const isObject = (value: any): value is object =>
        value && typeof value === 'object'
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Karol Majewski    6 年前

    让我们定义基础知识:

    type Primitive = boolean | number | string | symbol | null | undefined;
    
    
    class Foo<T> {
        constructor(value: T | (T extends Function ? never : T)) {}
    }
    
    new Foo(() => 1)
    new Foo(1)
    

    如果我们定义 isPrimitive isObject 像这样的:

    declare function isPrimitive(argument: unknown): argument is Primitive;
    declare function isObject<T extends object>(argument: T | Primitive): argument is T;
    

    我们在调用站点得到了正确的类型推断:

    function testPrimitive(argument: unknown) {
        if (isPrimitive(argument)) {
            return new Foo(argument);
        }
    }
    
    function testObject<T extends object>(argument: T | Primitive) {
        if (isObject(argument)) {
            return new Foo(argument);
        }
    }
    

    Playground