首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型化Guid不能用作索引

类型化Guid不能用作索引
EN

Stack Overflow用户
提问于 2021-11-05 16:25:29
回答 1查看 86关注 0票数 1

我们是一个TypeScript应用程序,正在考虑添加一个Guid类型,如下所示:

代码语言:javascript
复制
declare type Guid = string & { isGuid: true }

这样做的好处是,您不能意外地将任何字符串分配给Guid,至少在不先转换它的情况下是这样的。但它有以下问题:

代码语言:javascript
复制
const a: Record<Guid, number> = { '3fa85f64-5717-4562-b3fc-2c963f66afa6': 42 };
const b: Guid = '3fa85f64-5717-4562-b3fc-2c963f66afa6';
const c: number = a[b];
// Error: Element implicitly has an 'any' type because expression of type
// 'string & { isGuid: true; }' can't be used to index type 'Record<string & { isGuid: true; }, number>'.

有允许这样做的TypeScript设置吗?如果我不声明c的类型并悬停在它上面,它正确地消除了number。也许编辑器(VS代码)使用的是与TypeScript可执行文件不同的编译器?有什么方法修复它吗?

  • 操作系统: Windows 10
  • TypeScript版本: 3.7.0
  • NodeJS版本: 17.0.1
  • VSCode版本: 1.61.2
EN

回答 1

Stack Overflow用户

发布于 2021-11-06 07:47:20

下面的代码使用TypeScript 4.1。

这里已经很晚了,但你可以问我一些问题,我稍后会编辑答案。作为建议,您可能需要使用类型保护来代替下面的操作。

主要类型如下:

代码语言:javascript
复制
type GUID<
    s1 extends string = string,
    s2 extends string = string,
    s3 extends string = string,
    s4 extends string = string,
    s5 extends string = string
> = `${s1}-${s2}-${s3}-${s4}-${s5}`

它涵盖了大多数情况,但不涵盖的情况需要一个类型保护,我将其分成三个独立的函数:

代码语言:javascript
复制
function guidOctetPart<GP extends string, L extends number>(guidPart: GP, length: L): guidPart is string {
    return guidPart.length === length;
}

function isGuid(guid: string): guid is GUID {
    const [o1,o2,o3,o4,o5] = guid.split("-");
    return ([[o1, 8], [o2, 4], [o3, 4], [o4, 4], [o5, 12]] as const).every(([guidPart, length]) => guidOctetPart(guidPart, length))
}

function isGuidRecord (guid: Record<GUID, number>): guid is Record<GUID, number> {
    return Object.keys(guid).every(isGuid) && Object.values(guid).every(x => typeof x === "number");
}

更多的细节在操场链接和代码下面..。主要通过评论的使用。

操场链接(或查看下面的代码)

代码语言:javascript
复制
//  This could work if this issue gets fixed https://github.com/microsoft/TypeScript/issues/34692
// type GUID<
//     l1 extends number = number,
//     l2 extends number = number,
//     l3 extends number = number,
//     l4 extends number = number,
//     l5 extends number = number, 
//     s1 extends string = string,
//     s2 extends string = string,
//     s3 extends string = string,
//     s4 extends string = string,
//     s5 extends string = string
// > = `${s1 & {length: l1}}-${s2 & {length: l2}}-${s3 & {length: l3}}-${s4 & {length: l4}}-${s5 & {length: l5}}`
// For now we could do this... not as good but close enough.
type GUID<
    s1 extends string = string,
    s2 extends string = string,
    s3 extends string = string,
    s4 extends string = string,
    s5 extends string = string
> = `${s1}-${s2}-${s3}-${s4}-${s5}`

// Guid has too few parts
const Fails1: GUID = "5151515-1161616-171717-1717171" 

// Guid has too many parts 
const Fails2: GUID = "5151515-1161616-171717-1717171-12341414-414141414" 

// proper GUID is allowed
const Passes1: GUID = "12345678-1234-1234-1234-123456789012";

// caveat: Allows wrong lengthed-parts GUIDs
const Passes2: GUID = "1234-5151515-1161616-171717-1717171" 

// TO AVOID the above caveat you should use function type guards

// Show an obviously Failing record
if(isGuidRecord({["1234"]: 1})) {
    console.log("Won't ever log.") // type is always going to be a GUID if it enters here
}

// Proper GUID will log
if(isGuidRecord({[Passes1]: 2})) {
    console.log("Will always log.")
}

// improper guid will not log even though it passes type checks
if(isGuidRecord({[Passes2]: 2})) {
    console.log("Won't ever log.")
}

function guidOctetPart<GP extends string, L extends number>(guidPart: GP, length: L): guidPart is string {
    return guidPart.length === length;
}

function isGuid(guid: string): guid is GUID {
    const [o1,o2,o3,o4,o5] = guid.split("-");
    return ([[o1, 8], [o2, 4], [o3, 4], [o4, 4], [o5, 12]] as const).every(([guidPart, length]) => guidOctetPart(guidPart, length))
}

function isGuidRecord (guid: Record<GUID, number>): guid is Record<GUID, number> {
    return Object.keys(guid).every(isGuid) && Object.values(guid).every(x => typeof x === "number");
}

参见上述操场的JS输出:

代码语言:javascript
复制
"use strict";
// Guid has too few parts
const Fails1 = "5151515-1161616-171717-1717171";
// Guid has too many parts 
const Fails2 = "5151515-1161616-171717-1717171-12341414-414141414";
// proper GUID is allowed
const Passes1 = "12345678-1234-1234-1234-123456789012";
// caveat: Allows wrong lengthed-parts GUIDs
const Passes2 = "1234-5151515-1161616-171717-1717171";
// Show an obviously Failing record
if (isGuidRecord({ ["1234"]: 1 })) {
    console.log("Won't ever log."); // type is always going to be a GUID if it enters here
}
// Proper GUID will log
if (isGuidRecord({ [Passes1]: 2 })) {
    console.log("Will always log.");
}
// improper guid will not log
if (isGuidRecord({ [Passes2]: 2 })) {
    console.log("Won't ever log.");
}
function guidOctetPart(guidPart, length) {
    return guidPart.length === length;
}
function isGuid(guid) {
    const [o1, o2, o3, o4, o5] = guid.split("-");
    return [[o1, 8], [o2, 4], [o3, 4], [o4, 4], [o5, 12]].every(([guidPart, length]) => guidOctetPart(guidPart, length));
}
function isGuidRecord(guid) {
    return Object.keys(guid).every(isGuid) && Object.values(guid).every(x => typeof x === "number");
}

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69856338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档