简体   繁体   中英

Indexing into Typescript Record with opaque type

The following code is giving me a TypeScript error:

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"
}

I'm using UserID as an opaque type (like described here https://evertpot.com/opaque-ts-types/ ), so it acts a string, however random strings cannot be assigned to a variable of type UserID.

However, I'm getting the following error on the last line: "Element implicitly has an 'any' type because expression of type 'UserID' can't be used to index type 'Record<UserID, Chat>'."

Any idea why I'm getting this error? I don't see why I can't use something of type UserID into index into a record of type Record<UserID, Chat>.

UPDATE: According to this page https://levelup.gitconnected.com/building-type-safe-dictionaries-in-typescript-a072d750cbdf , it looks like I can achieve the behavior I want by using Javascript Maps instead of objects. But this means giving up on all the object syntax that Javascript offers, which I'd rather not do.

UPDATE2: Looks like this might be a known issue https://github.com/microsoft/TypeScript/issues/15746 that Typescript doesn't handle right now

Indeed this is not possible. This is due to the fact that objects can only have numeric, string or symbol keys and not other, non-primitive types.

Even if it compiles down to something that would work, typescript in this case can't know this due to the earlier unique symbol trick.

Using a Map is probably indeed the easiest way to do this. Maps have a pretty nice API that might not find annoying.

The solution may be template literals as indexes. But this feature is not supported yet. However, it is in current milestone (TS 4.3.(0,1)). Check this out: 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',
}

Related issues:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM