我们是一个TypeScript应用程序,正在考虑添加一个Guid类型,如下所示:
declare type Guid = string & { isGuid: true }这样做的好处是,您不能意外地将任何字符串分配给Guid,至少在不先转换它的情况下是这样的。但它有以下问题:
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可执行文件不同的编译器?有什么方法修复它吗?
发布于 2021-11-06 07:47:20
下面的代码使用TypeScript 4.1。
这里已经很晚了,但你可以问我一些问题,我稍后会编辑答案。作为建议,您可能需要使用类型保护来代替下面的操作。
主要类型如下:
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}`它涵盖了大多数情况,但不涵盖的情况需要一个类型保护,我将其分成三个独立的函数:
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");
}更多的细节在操场链接和代码下面..。主要通过评论的使用。
// 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输出:
"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");
}
https://stackoverflow.com/questions/69856338
复制相似问题