【问题标题】:How to define a Typescript function that swaps values of two object properties by name, with check of type compatibility?如何定义一个 Typescript 函数,通过名称交换两个对象属性的值,并检查类型兼容性?
【发布时间】:2019-08-12 18:22:21
【问题描述】:

我正在尝试定义一个函数,该函数在给定名称的对象上交换两个属性的值,但我希望编译器检查类型兼容性(或至少检查两个属性是否具有相同的类型):

function swap<T, TKey1 extends keyof T, TKey2 extends keyof T>(obj: T, key1: TKey1, key2: TKey2): void{
    let temp = obj[key1];
    obj[key1] = obj[key2]; 
    obj[key2] = temp;
}


let obj = {
    a: 1,
    b: 2,
    c: ""
}

swap(obj, "a", "b");    // good, both are numbers
swap(obj, "a", "c");    // should not compile, swapping number with string

TS playground

我得到了以下结果,但它需要 obj 传递两次。

function swap<T,
    TKey1 extends keyof T,
    TKey2 extends keyof T,
    TIn extends { [p in TKey1|TKey2]: T[TKey1] } >(_:T, obj: TIn, key1: TKey1, key2: TKey2): void{
    let temp = <any>obj[key1];
    obj[key1] = <any>obj[key2]; 
    obj[key2] = temp;
}


let obj = {
    a: 1,
    b: 2,
    c: ""
}

swap(obj, obj, "a", "b");    // good, both are numbers
swap(obj, obj, "a", "c");    // error, as expected

TS playground

另外,如果我返回一个函数,我可以使用条件类型来达到预期的结果,但是很容易忘记第二次调用。

function swap<T,
    TKey1 extends keyof T,
    TKey2 extends keyof T>(obj: T, key1: TKey1, key2: TKey2):
                                            T[TKey1] extends T[TKey2] ? T[TKey2] extends T[TKey1] 
                                                ? () => void
                                                : never : never {

    return <any>(() => {
        let temp = <any>obj[key1];
        obj[key1] = <any>obj[key2];
        obj[key2] = temp;
    });
}


let obj = {
    a: 1,
    b: 2,
    c: ""
}

swap(obj, "a", "b")();    // good, both are numbers
swap(obj, "a", "c")();    // error, as expected

TS playground

上面的例子可以简化吗?我可以提供某种类型而不是 never 来指示类型系统错误吗?

附:我知道 [obj.a, obj.b] = [obj.b, obj.a]; ,但想避免它。

【问题讨论】:

    标签: typescript types swap


    【解决方案1】:

    好的,他们的键原来是用高级类型过滤第二个键。

    提供源代码:https://github.com/IKoshelev/ts-typing-util/blob/master/src/Swap.ts

    NPM 安装npm i ts-typing-util

    export type SwappableKeys<T, TKey1 extends keyof T> = Exclude<{
        [key in keyof T]:
        /**/ T[key] extends T[TKey1]
        /**/ ? T[TKey1] extends T[key]
        /*      */ ? key
        /*      */ : never
        /**/ : never;
    
    }[keyof T], TKey1>;
    
    /**
     * Swap prop values with a check that values have compatible type
     * @example
     * const t = {
     *   a: 1,
     *   b: 2,
     *   c: '',
     *   c1: '',
     *   d: { a: 5 },
     *   e: { a: 6 },
     *   f: { b: 7 },
     *   g: { a: '' }
     * }
     *
     * swap(t, 'a', 'b');
     * swap(t, 'a', 'c'); //error
     * swap(t, 'b', 'c'); //error
     * swap(t, 'a', 'a'); //error
     * swap(t, 'c', 'c1');
     * swap(t, 'd','e');
     * swap(t, 'd','f'); //error
     * swap(t, 'd','g'); //error
     **/
    export function swap<T, TKey1 extends keyof T>(inst: T, key1: TKey1, key2: SwappableKeys<T, TKey1>): void {
    
        const buff = inst[key1] as any;
        inst[key1] = inst[key2] as any;
        inst[key2] = buff;
    
    }
    
    

    【讨论】:

      猜你喜欢
      • 2018-07-29
      • 2022-07-23
      • 2019-09-18
      • 2020-07-24
      • 2013-01-02
      • 1970-01-01
      • 1970-01-01
      • 2021-10-23
      • 2022-12-18
      相关资源
      最近更新 更多