【问题标题】:Indexing into Typescript Record with opaque type使用不透明类型对 Typescript 记录进行索引
【发布时间】:2020-07-03 21:39:33
【问题描述】:

以下代码给了我一个 TypeScript 错误:

type UserID = string & { readonly _: unique symbol };

interface Chat {
  name: string
}

type AllChats = Record<UserID, Chat>;

const test: AllChats = {}
const userID = "ads" as UserID;

test[userID] = {
  name: "my chat"
}

我将 UserID 用作不透明类型(如此处所述 https://evertpot.com/opaque-ts-types/),因此它充当字符串,但是不能将随机字符串分配给 UserID 类型的变量。

但是,我在最后一行收到以下错误:“元素隐式具有 'any' 类型,因为 'UserID' 类型的表达式不能用于索引类型 'Record'。 "

知道为什么我会收到此错误吗?我不明白为什么我不能使用 UserID 类型的东西来索引 Record 类型的记录。

更新:根据这个页面https://levelup.gitconnected.com/building-type-safe-dictionaries-in-typescript-a072d750cbdf,看起来我可以通过使用 Javascript Maps 而不是对象来实现我想要的行为。但这意味着放弃 Javascript 提供的所有对象语法,我不想这样做。

UPDATE2:看起来这可能是 Typescript 目前无法处理的已知问题 https://github.com/microsoft/TypeScript/issues/15746

【问题讨论】:

  • 在访问之前您需要您的断言,否则不透明将毫无意义。 Iow:创建validUserId函数或者isValidUserId在访问前检查。
  • 访问前需要断言是什么意思?
  • 如果我已经确定userID是UserID类型,为什么我需要在访问之前再次验证它?

标签: javascript reactjs typescript


【解决方案1】:

这确实是不可能的。这是因为对象只能有数字、字符串或符号键,而不能有其他非原始类型。

即使它编译成可以工作的东西,在这种情况下,打字稿也无法知道这一点,因为早先的 unique symbol 技巧。

使用Map 可能确实是最简单的方法。地图有一个相当不错的 API,可能不会让人讨厌。

【讨论】:

    【解决方案2】:

    2022 年 1 月 29 日更新,TypeScript 4.5.4:它有效。

    type OPAQUE_MODIFIER = '_';
    export type Opaque<T extends string> = `${OPAQUE_MODIFIER}${T}${OPAQUE_MODIFIER}${string}`
    
    type Apple = Opaque<'apple'>;
    type Orange = Opaque<'orange'>;
    
    
    // Test 1
    const apple: Apple = '1' as Apple;
    // @ts-expect-error
    const orange: Orange = apple;
    
    // Test 2
    const appleCollection: Record<Apple, string> = {
        [apple]: 'value',
        // @ts-expect-error <----------- The only one case that fails :(
        [orange]: 'value',
    };
    
    // Test 3
    appleCollection[apple] = 'newValue';
    // @ts-expect-error
    appleCollection[orange] = 'newValue';
    

    解决方案可能是模板文字作为索引。但目前还不支持此功能。但是,它处于当前里程碑 (TS 4.3.(0,1))。看看这个:https://github.com/microsoft/TypeScript/issues/42192

    type OPAQUE_MODIFIER = '__OPAQUE__';
    type EntityId = `${OPAQUE_MODIFIER}user${OPAQUE_MODIFIER}${string}`;
    
    type Entity = {
        title: string,
    };
    
    type EntityCollection = Record<EntityId, Entity>;
    
    const collection: EntityCollection = {
        // should works as expected (1)
        ['entity 1 id' as EntityId]: {
            title: 'entity 1',
        },
    
        // should throws an error (2)
        ['entity 2 id' as string]: {
            title: 'entity 2',
        },
    };
    
    // should works as expected (3)
    collection['entity 3 id' as EntityId] = {
        title: 'entity 3',
    }
    
    // should throws an error (4)
    collection['entity 4 id' as string] = {
        title: 'entity 4',
    }
    

    相关问题:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 1970-01-01
      • 1970-01-01
      • 2014-02-18
      • 2017-08-04
      • 2021-02-22
      • 1970-01-01
      相关资源
      最近更新 更多