【问题标题】:Generic selector function for object properties对象属性的通用选择器函数
【发布时间】:2021-02-20 00:27:43
【问题描述】:

我最近开始使用 TypeScript 编写代码,但遇到了一个我似乎无法找到答案的问题。

考虑下面的代码:

interface testInterface {
  a: string;
  b: number;
  c?: number;
}

const testObject: testInterface = {
  a: "1",
  b: 2,
};

function selectorFunction<GenericKey extends keyof testInterface>(
  key: GenericKey
): Required<testInterface>[keyof testInterface] {
  if (testObject[key]) return testObject[key];
  throw new Error("Error");
}

解释一下我想要实现的目标:selectorFunction 应该根据接收到的键从testObject 返回一个值,同时还具有该值的正确类型。这就是关键属性类型是 Generic 的原因。

就其本身而言,它工作正常,当可选属性发挥作用时问题就开始了,因为方法返回类型是必需的,因此该方法不会返回任何未定义的值。

理论上应该很容易,只需添加一个 if 来检查该属性是否存在,它应该可以工作,但是 typescript 仍然给我以下错误:

  Type 'string | number | undefined' is not assignable to type 'string | number'.
    Type 'undefined' is not assignable to type 'string | number'.
      Type 'testInterface[GenericKey]' is not assignable to type 'number'.
        Type 'string | number | undefined' is not assignable to type 'number'.
          Type 'undefined' is not assignable to type 'number'.ts(2322)

【问题讨论】:

    标签: typescript typescript-typings typescript-generics


    【解决方案1】:

    我认为以下内容可以满足您的要求。此外,每当您输入会导致错误的键时,它都会为您提供never 作为返回类型。 (Playground link)

    interface testInterface {
        a: "a"
        b: "b"
        c?: "c"
    }
    
    
    const testObject: testInterface = {
        a: "a",
        b: "b",
    }
    
    
    // https://stackoverflow.com/questions/50829805/get-keyof-non-optional-property-names-in-typescript
    type RequiredKeys<T> = {
        [k in keyof T]-?: undefined extends T[k] ? never : k
    }[keyof T]
    
    
    type GetSafely<T, K extends keyof T> =
        K extends RequiredKeys<T>
            ? T[K]
            : never
    
    
    // <---- For documentation/testing purposes only
    
    
        // type is "a" | "b" - as intended
        type ReqK = RequiredKeys<testInterface>
        // type is `"a"` - as intended
        type A = GetSafely<testInterface, "a">
        // type is `"b"` - as intended
        type B = GetSafely<testInterface, "b">
        // type is `never` - as intended
        type C = GetSafely<testInterface, "c">
    
    // ---->
    
    
    const getSafely = <T extends {}>(obj: T) =>
        <K extends keyof T>(key: K): GetSafely<T, K> => {
            if (obj[key] !== undefined) {
                return obj[key] as GetSafely<T, K>
            }
    
            throw new Error("Error");
        }
    
    // key is correctly required to be in `"a" | "b" | "c"`
    const selectorFunction = getSafely(testObject)
    
    
    // <---- For documentation/testing purposes only
    
        // type is `"a"` - as intended
        const a = selectorFunction("a")
        // type is `"b"` - as intended
        const b = selectorFunction("b")
        // type is `never` - as intended
        const c = selectorFunction("c")
    
    // ---->
    

    【讨论】:

    • 在对象中不存在参数c的情况下似乎可以工作,但是当它存在时呢?我的目标是当它存在于对象中时安全地获取它,但目前 c 的类型始终是 never。另外,我试图找到一种无需类型转换的方法,因为在某些情况下它可能被认为是不安全的。
    猜你喜欢
    • 2022-11-04
    • 1970-01-01
    • 2016-05-29
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-11
    • 1970-01-01
    相关资源
    最近更新 更多