代码之家  ›  专栏  ›  技术社区  ›  Thiago Pereira Maia

验证对象键与值的字符串内容之间的相关性

  •  1
  • Thiago Pereira Maia  · 技术社区  · 1 年前

    我试图进行一个复杂的验证,我希望对象的键的名称出现在分配给该对象的值的字符串中。

    const DatoButtonColumnSectionFragment = "fragment DatoNotSameKeyFragment {}"
    const DatoCatalogSectionFragment = "fragment DatoCatalogSectionFragment {}"
    
    type SectionFragmentMapGeneric = {
      [K in `Dato${string}SectionFragment`]: string
    }
    
    type SectionFragmentMap<T extends SectionFragmentMapGeneric> = {
      [K in keyof T]: K extends string ? `${string}${K}${string}` : never
    }
    
    // Declare SectionFragments object without immediate type enforcement
    const SectionFragmentsUnvalidated = {
      DatoButtonColumnSectionFragment,
      DatoCatalogSectionFragment,
    } as const satisfies SectionFragmentMapGeneric
    
    const SectionFragments =
      SectionFragmentsUnvalidated satisfies SectionFragmentMap<
        typeof SectionFragmentsUnvalidated
      >
    

    我让它像这样工作,但分两步完成的满意度并不高,因为它增加了复杂性,而且IDE无法突出显示错误的确切关键。有办法解决这个问题吗?

    如果我不使用 T extends SectionFragmentMapGeneric ,它将接受任何匹配的字符串 Dato${string}SectionFragment ,并且不会将特定键与特定值相关联。如果我将第二个满足项移动到初始声明,则映射变为 any 因为它是在自己的初始化器上使用的。

    1 回复  |  直到 1 年前
        1
  •  1
  •   jcalz    1 年前

    由于TypeScript中没有与您的需求相对应的特定类型,因此您需要使用 generic type TypeScript目前不知道如何在泛型中推断泛型类型参数 类型 (根据要求 microsoft/TypeScript#32794 ),所以最好的办法就是使用泛型 功能 。您可以编写一个只返回其输入的通用辅助标识函数,该函数使用 constraint 提供您想要的支票。可能是这样的:

    const asSectionFragmentMap = <T extends { [K in keyof T]:
      K extends `Dato${string}SectionFragment` ? `${string}${K}${string}` : never
    }>(t: T) => t;
    

    这是一个递归约束。当你打电话的时候 asSectionFragmentMap(obj) TypeScript将确保 obj 与映射的类型匹配。对于每个密钥 K ,它通过检查 conditional type 如果密钥匹配 `Dato${string}SectionFragment` 或者不。如果没有,则检查属性类型 never 因此,无论如何,它都会拒绝该财产。如果是这样,则检查属性类型 `${string}${K}${string}` 。让我们来测试一下:

    const DatoButtonColumnSectionFragment = "fragment DatoNotSameKeyFragment {}"
    const DatoCatalogSectionFragment = "fragment DatoCatalogSectionFragment {}"
    
    const sectionFragments = asSectionFragmentMap({
      DatoButtonColumnSectionFragment,
      // Type '"fragment DatoNotSameKeyFragment {}"' is not assignable to type 
      // '`${string}DatoButtonColumnSectionFragment${string}`'.
      DatoCatalogSectionFragment,
      Oopsie: "abc" // error!
      //~~~~ <-- Type 'string' is not assignable to type 'never'.
    });
    

    看起来不错。 DatoButtonColumnSectionFragment 类型错误,错误消息反映了这一点。 DatoCatalogSectionFragment 是可以接受的。自从 Oopsie 不匹配 Dato⋯SectionFragment ,这也是一个错误。

    Playground link to code