【发布时间】:2019-09-05 19:42:17
【问题描述】:
TLDR;版本
编辑,我在底部添加了一个 Playground,链接
我在 TypeScript 中有一个数组 ob 对象。
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];
现在我在问是否可以编写一个类型,它是该数组中所有 names 的联合:
type Name<Converters[]> = "???";
// type Name<MyConverters> = "info" | "pressure"
和一个对应名称的泛型类型
type Value<Converters[], name extends string>
type Value<MyConverters, "pressure"> = number;
解释我在做什么以及为什么
我正在写一个Bluetooth Low Energy (BLE) Library for Node called Sblendid,您可以通过它与 BLE 外围设备通信。 BLE 外设具有Services,您可以从中读取和写入值(除其他外)。此类服务的“端点”称为Characteristics,它们具有 ID (UUID)。
当你读到一个特性时,你需要说出你想要寻址的 UUID,返回值将是二进制数据(通常是 Node 中的 Buffer)。
因此,为了让图书馆用户的生活更轻松,我想创建一个 API,让他们提供我称之为 Converters 的内容,如下所示:
interface Converter<T = Buffer> {
name: string;
uuid: CUUID;
decode: (value: Buffer) => T;
}
type Converters = Converter<any>[];
所以现在当有人想与外围设备上的Service 交谈时,他们应该能够将他们的转换器传递给Service,现在可以通过名称来寻址他们的Characteristics,并将获得他们的解码值而不是二进制数据时读取一个Characteristic,像这样:
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 中编写所有内容时,我需要正确输入。
我想要实现的是,如果用户提供converters,如示例所示,则:
const service = new Service(converters);
const info = await service.read("infoasdasd");
// ^^^^^^^^^^
应该触发一个错误,指出这不是您的 converters 数组中的名称。 TypeScript 也应该明白这一点
const service = new Service(converters);
const info = await service.read("infoasdasd");
// ^^^^
是一个字符串,因为你在converters 中写了什么。
因为这一切看起来很容易,我希望用户能够在没有converters 的情况下工作。因此,如果他们创建一个没有converters 的Service
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
}
}
当然,这不是 BLE 的实际实现。使用此代码,我试图正确输入。所以我想告诉read它可以接受NameOrCUUID,这取决于Service类是否有一个Converters的泛型name或Converters或如果没有泛型传递给 Service 类将接受任何特征 UUID。
这行得通。当我调用此代码时:
const service = new Service(converters);
const info = service.read("info");
const service2 = new Service();
const info2 = service2.read("180a");
并从那里传递converters 作为参数,它会告诉我我只能传递info 或pressure。我根本不传递任何转换器 (service2) 我可以传递任何数字或字符串。
但这只是假的,因为实现
type Name<C extends Converters> = "???";
type Value<C, N extends NameOrCUUID<C>> = "???";
对于这个,这个和这个是我想不通的部分。
我需要告诉TypeScript 允许Converters 中任何项目的name 属性与值一样,并且值必须与名称匹配。
好吧,对不起,文字墙。我希望我能表达我的要求(最后一段是实际问题)。我想知道这在 TypeScript 中是否可行。
这里是游乐场:
【问题讨论】:
-
"tuple" 可能是一个不错的关键字添加到此处(即
MyConverters是什么)
标签: typescript