TypeScript:数组中项的特定属性的并集
TLDR;版本 编辑,我添加了一个游乐场,链接在底部 我在TypeScript中有一个ob对象数组TypeScript:数组中项的特定属性的并集,typescript,Typescript,TLDR;版本 编辑,我添加了一个游乐场,链接在底部 我在TypeScript中有一个ob对象数组 interface Converter<T = Buffer> { name: string; uuid: CUUID; decode: (value: Buffer) => T; } const infoConverter: Converter<string> = { name: "info", uuid: "180a", decode:
interface Converter<T = Buffer> {
name: string;
uuid: CUUID;
decode: (value: Buffer) => T;
}
const infoConverter: Converter<string> = {
name: "info",
uuid: "180a",
decode: v => v.toString()
};
const pressureConverter: Converter<number> = {
name: "pressure",
uuid: "1810",
decode: v => v.readInt32BE(0)
};
type MyConverters = [Converter<string>, Converter<number>]
const converters: MyConverters = [infoConverter, pressureConverter];
以及对应于名称的泛型类型
type Value<Converters[], name extends string>
type Value<MyConverters, "pressure"> = number;
因此,现在,当有人想在外围设备上与服务
通话时,他们应该能够将转换器传递到服务
,现在可以通过名称寻址他们的特征
,并在读取特征
时获得解码值,而不是二进制数据,如下所示:
const infoConverter: Converter<string> = {
name: "info",
uuid: "180a",
decode: v => v.toString()
};
const pressureConverter: Converter<number> = {
name: "pressure",
uuid: "1810",
decode: v => v.readInt32BE(0)
};
const converters: Converters = [infoConverter, pressureConverter];
const service = new Service(converters);
const info = await service.read("info");
// ^^^^ ^^^^
// this will be a string their name instead of a UUID (like "a002")
应触发一个错误,说明这不是转换器
数组中的名称。TypeScript还应该理解
const service = new Service(converters);
const info = await service.read("infoasdasd");
// ^^^^
是一个字符串,因为您在转换器中编写了内容
因为这一切看起来都很简单,我希望用户能够在没有转换器的情况下工作。因此,如果他们创建了一个服务
,而没有转换器
const service = new Service();
const info = service.read("180a");
// ^^^^ ^^^^
// Buffer any CUUID (string | number)
它们应该能够传递任何特征UUID,并将获得一个缓冲区作为返回值。实施是有效的。我正在努力教TypeScript
如何打字
到目前为止,我得到了这个:
type CUUID = number | string;
interface Converter<T = Buffer> {
name: string;
uuid: CUUID;
decode: (value: Buffer) => T;
}
type Converters = Converter<any>[];
type NameOrCUUID<C> = C extends Converters ? Name<C> : CUUID;
type Name<C extends Converters> = "info" | "pressure";
type Value<C, N extends NameOrCUUID<C>> = "" | Buffer;
class Service<C> {
private converters?: Converters;
constructor(converters?: C) {
this.converters = converters as any; // Ask on StackOverflow
}
public read<N extends NameOrCUUID<C>>(name: N): Value<C, N> {
if (this.converters) {
return this.getConvertedValue(name);
} else {
return this.getBufferForUUID(name);
}
}
private getBufferForUUID(uuid: CUUID): Buffer {
return Buffer.from("Foobar", "utf8"); // Dummy Code
}
private getConvertedValue<N extends NameOrCUUID<C>>(name: N): Value<C, N> {
return "Foobar"; // Dummy Code
}
}
然后将转换器
作为一个参数传递到上面,它会告诉我只能传递信息
或压力
。我根本不通过任何转换器(service2
),我可以通过任何数字或字符串
但这只是假的,因为
type Name<C extends Converters> = "???";
type Value<C, N extends NameOrCUUID<C>> = "???";
type Name=“?”;
类型值=“???”;
对于这个,这个,这是我无法理解的部分
我需要告诉TypeScript
以允许Converters
中任何项的name
属性与值类似,并且值必须与名称匹配
好的,对不起,这是文字墙。我希望我能表达我的问题(最后一段才是真正的问题)。我想知道这在打字脚本中是否可行
interface Converter<T = Buffer> {
name: string;
uuid: CUUID;
decode: (value: Buffer) => T;
}
const infoConverter: Converter<string> = {
name: "info",
uuid: "180a",
decode: v => v.toString()
};
const pressureConverter: Converter<number> = {
name: "pressure",
uuid: "1810",
decode: v => v.readInt32BE(0)
};
type MyConverters = [Converter<string>, Converter<number>]
const converters: MyConverters = [infoConverter, pressureConverter];
这里是一个游乐场:
我看到的第一个问题是infoConverter
作为Converter
和pressureConverter
作为Converter
的类型注释太宽。编译器将尽职尽责地忘记infoConverter.name
是“info”
,而是使用表示它是字符串的类型注释
我认为,只要使用类型推断和a来为转换器实例获取尽可能狭窄的类型,您就会更接近您想要的:
const infoConverter = {
name: "info",
uuid: "180a",
decode: (v: Buffer) => v.toString()
} as const;
const pressureConverter = {
name: "pressure",
uuid: "1810",
decode: (v: Buffer) => v / 1024
} as const;
const converters = [infoConverter, pressureConverter] as const;
此时,不能保证转换器的元素符合转换器
接口,但您可以确保任何接受转换器
的函数或接受类型转换器
的类型都被约束为转换器“tuple”可能是此处添加的一个好关键字(即MyConverters
是什么)我有一个问题。我理解为什么const
赋值是必要的,但当我把它注释出来时,它仍然有效。我不明白,因为你说的完全有道理!无法发布链接,因为url太长,但如果你这样做const converters=[infoConverter,pressureConverter];//作为常量;
在第22行它仍然有效。infoConverter
和pressureConverter
定义中的const
断言非常有用,因为没有它们,您需要其他方法来确保的“信息”
和的“压力”
名称不会变宽。但是一旦infoConverter
和pressureConverter
已经变窄,您确实不需要在Converter
定义中使用const
断言,因为所做的只是保留两个条目的顺序,而您的代码并不真正关心这一点。我不会说as const
是必要的;这只是缩小推论范围的最方便的方法之一。非常感谢您的解释,您是一个传奇!
const service = new Service(converters);
const info = service.read("info");
const service2 = new Service();
const info2 = service2.read("180a");
type Name<C extends Converters> = "???";
type Value<C, N extends NameOrCUUID<C>> = "???";
const infoConverter = {
name: "info",
uuid: "180a",
decode: (v: Buffer) => v.toString()
} as const;
const pressureConverter = {
name: "pressure",
uuid: "1810",
decode: (v: Buffer) => v / 1024
} as const;
const converters = [infoConverter, pressureConverter] as const;
type Names<C extends ReadonlyArray<Converter<any>>> = C[number]["name"];
type Value<
C extends ReadonlyArray<Converter<any>>,
N extends Names<C>
> = Extract<C[number], { name: N }> extends Converter<infer V> ? V : never;
type TheNames = Names<typeof converters>; // "info" | "pressure"
type InfoValue = Value<typeof converters, "info">; // string
type PressureValue = Value<typeof converters, "pressure">; // number