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

将TypeScript泛型约束到另一个泛型的属性的键

  •  0
  • jnpdx  · 技术社区  · 3 年前

    我设置了以下接口和对象:

    interface ServicesList {
        [key: string]: Service;
    }
    
    interface Service {
        uuid: string;
        characteristics: CharacteristictList;
    }
    
    interface CharacteristictList {
        [key: string]: string;
    }
    
    const ServiceAndCharacteristicMap: ServicesList = {
        firstService: {
            uuid: '0x100',
            characteristics: {
                characteristicOne: '0x0101',
            },
        },
        secondService: {
            uuid: '0x200',
            characteristics: {
                secondCharacteristic: '0x0201'
            }
        }
    };
    

    然后,我定义了以下函数:

    function sendCharacteristic<T extends Service, K extends keyof T['characteristics']>(props: {
            service: T;
            characteristic: K;
        }) {
            console.log(props.service.uuid, 
                        props.service.characteristics[props.characteristic])
    }
    

    目前,TypeScript通过以下编译时错误对此进行了抱怨:

    类型“K”不能用于索引类型“CharacteristictList”

    我的目标是约束第二个参数( characteristic )这样我就有了我使用的钥匙的打字安全。例如,以下操作应该成功:

    //should succeed
    sendCharacteristic({
        service: ServiceAndCharacteristicMap.firstService,
        characteristic: 'characteristicOne'
    });
    

    但是,这应该会失败,因为 characteristicOne 属于 firstService 我路过 secondService 对于第一个参数:

    //should fail since characteristicOne belongs to firstService
    sendCharacteristic({
        service: ServiceAndCharacteristicMap.secondService,
        characteristic: 'characteristicOne'
    });
    

    目前 sendCharacteristic 抱怨编译错误。

    如何正确约束 特征 参数,以便获得的特定实例的类型安全性 Service 我路过?

    TypeScript playground with all included code

    0 回复  |  直到 3 年前
        1
  •  2
  •   Titian Cernicova-Dragomir    3 年前

    代码的主要问题是,您既没有保留要分配给的常量的类型 ServiceAndCharacteristicMap

    如果完全删除注释,代码将按预期工作。 Playground Link

    您也可以使用函数来约束 服务和特性映射 将来 ServicesList 同时保留类型

    
    function makeServicesList<T extends ServicesList>(o: T){
        return o;
    }
    
    const ServiceAndCharacteristicMap = makeServicesList({
        firstService: {
            uuid: '0x100',
            characteristics: {
                characteristicOne: '0x0101',
            },
        },
        secondService: {
            uuid: '0x200',
            characteristics: {
                secondCharacteristic: '0x0201'
            }
        }
    });
    

    Playground Link