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

缩小类型脚本索引类型

  •  0
  • Terite  · 技术社区  · 6 年前

    我有以下代码应该编译,而不是:

    interface A {
        field: string;
        fieldB: { fieldC: string;}
    }
    
    function mixPropValue<P extends keyof A>(propName: P, value: A[P]) {
        if (propName === 'fieldB') {
            // value is always in type of { fieldC:string }
            value.fieldC; // Error: Property 'fieldC' does not exist on type 'A[P]'.
        }
    }
    

    Playground

    是否可以让typescript知道在第一个 if 分支机构?当然,我可以使用类型转换来编译代码,但我想避免这种情况。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Terite    6 年前

    可以在检查属性名的同时缩小类型的范围,以便将类型解析为更窄的选项:

    interface A {
        field: string;
        fieldB: { fieldC: string;}
    }
    
    function isFieldB<P extends keyof A>(propName: P, prop: { fieldC: string } | any): prop is { fieldC: string } {
        return propName === 'fieldB';
    }
    
    function mixPropValue<P extends keyof A>(propName: P, value: A[P]) {
        if (isFieldB(propName, value)) {
            value.fieldC;
        }
    }
    

    编译器的未来版本可能会使这一点变得多余,但如果他们这样做,这将是一个令人印象深刻的飞跃,因为这里有相当数量的“连接点”。不过,typescript团队经常管理它。

        2
  •  0
  •   Titian Cernicova-Dragomir    6 年前

    编译器不会缩小基于吸吮测试的类型,最好是自定义类型保护:

    function isProp<P extends keyof A>(value: A[keyof A], targetProp: P, testProp : keyof A) : value is A[P] {
        return targetProp === testProp;
    }
    function mixPropValue<P extends keyof A>(propName: P, oldValue: A[P], newValue: A[P]) {
        if (isProp(oldValue, 'fieldB', propName) && isProp(newValue, 'fieldB', propName)) {
            // oldValue, newValue here are always in type of { fieldC:string }
            return { ...oldValue, ...newValue }; // Error: Spread types may only be created from object types.
        }
        return newValue;
    }