Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/420.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何告诉typescript{type:enum,[type:string]:value}的所有可能组合都在我的类型中?_Javascript_Typescript - Fatal编程技术网

Javascript 如何告诉typescript{type:enum,[type:string]:value}的所有可能组合都在我的类型中?

Javascript 如何告诉typescript{type:enum,[type:string]:value}的所有可能组合都在我的类型中?,javascript,typescript,Javascript,Typescript,我能想到的最简单的例子是: interface text { type: "text"; text: string; } interface integer { type: "integer"; integer: number; } type Config = text | integer; function appendToBody(config: Config) { if (!config[config.type]) return; document.body.ap

我能想到的最简单的例子是:

interface text {
  type: "text";
  text: string;
}
interface integer {
  type: "integer";
  integer: number;
}
type Config = text | integer;

function appendToBody(config: Config) {
  if (!config[config.type]) return;
  document.body.append(config[config.type]);
}

function createConfig(type: "text" | "integer", value: string | number) {
  // how to let TS know that type and [type] will be matching?
  // TS naturally assumes { type: 'text', text: 5 } is possible, even though it isn't
  const config: Config = {
    type,
    [type]: value
  };
  appendToBody(config);
}
createConfig("text", "hello world");

基本上,我使用的模式是通过请求obj[obj.type]来提取值。 这对于我的实际情况很有用,因为我可以创建一个通用解析器,根据类型提取所需的值。它还有一个优点,即在类型更改时不必清空,因为它将保存在另一个[type]上,如果您重新更改,则不会丢失旧值


我只是不知道如何让typescript理解所有可能的类型和[type]的组合都包含在“Config”类型中。

让您的
类型配置变得灵活,强制它接受任何格式为
{type:string,[anything]:anything}
的类型

type Config = text | integer | {type: string, [key: string]: any};

首先,让我们明确一下,
integer
应该有一个名为
integer
的键,而不是
number
,对吗?像这样:

interface text {
    type: "text";
    text: string;
}
interface integer {
    type: "integer";
    integer: number; // right?
}
type Config = text | integer;

function appendToBody(config: Config) {
    if (!config[config.type]) return;
    document.body.append(config[config.type]);
}
好的


createConfig()
存在两个类型安全问题。。。一个是为调用方强制执行类型安全,另一个是在实现中强制执行类型安全。现在,编译器在实现内部警告您,它无法验证
{type:type,[type]:value}
是否为有效的
配置。现在警告您这一点是正确的,因为呼叫者可以无误地执行以下操作:

createConfig("text", 5); // no error, oops!
对于调用方和实现方来说,没有简单的方法可以解决这个问题。每一方都有自己的问题


要为调用者修复它,您可以使用以下命令:

function createConfig(type: "text", value: string): void;
function createConfig(type: "integer", value: number): void;
function createConfig(type: string, value: any): void {
    // impl
}
这很容易理解,但需要为
Config
联合的每个组成部分添加重载。您也可以使用类型和函数,如下所示:

type Lookup<T, K> = K extends keyof T ? T[K] : never;
type ConfigFor<T extends Config['type']> = Extract<Config, { type: T }>;
function createConfig<T extends Config['type']>(
    type: T,
    value: Lookup<ConfigFor<T>, T>
) {
  // impl
}

要为实现修复它(这是您的实际问题),编译器仍然无法确定
config
是有效的
config
,即使使用了固定的调用签名。对于重载,这是因为实现签名过于松散,无法表达约束,并且重载了实现。对于泛型条件类型,这是因为条件类型也依赖于它们中未解析的泛型类型参数。因此,在这两种情况下,编译器基本上放弃了在实现中强制执行相关数据类型的类型安全性。我经常想知道一些机制,它允许您通过控制流分析提示编译器遍历联合类型,但现在这只是一个幻想

那么,你能做什么?就我所知,在这方面真正只有两条路可走。您可以使用:保持代码不变,但只需告诉编译器您将负责确保类型安全,如:

const configAsserted = {
    type,
    [type]: value
} as any as Config;
appendToBody(configAsserted);
或者,您在运行时执行额外的手动检查,使编译器确信您所做的是安全的,如:

let configManual: Config;
if (type === "integer" && typeof value === "number") {
    configManual = { type: "integer", integer: value };
} else if (type === "text" && typeof value === "string") {
    configManual = { type: "text", text: value };
} else {
    throw new Error("YOU MESSED UP");
}
appendToBody(configManual);
两种方法都有效。在
Config
中添加成分时,断言不太安全,但可扩展性更好。手动检查是安全的,但它是多余的,每次向
Config
添加组件时都必须添加代码


在我看来,这是你的选择。就个人而言,我会选择更好的缩放解决方案,如:

type Lookup<T, K> = K extends keyof T ? T[K] : never;
type ConfigFor<T extends Config['type']> = Extract<Config, { type: T }>;
function createConfig<T extends Config['type']>(
    type: T,
    value: Lookup<ConfigFor<T>, T>
) {
    const config = {
        type,
        [type]: value
    } as any as Config;
    appendToBody(config);
}
type Lookup=K扩展了keyt?T[K]:从不;
类型ConfigFor=Extract;
函数createConfig(
类型:T,
值:查找
) {
常量配置={
类型,
[类型]:值
}与配置一样;
附体(配置);
}


好的,希望能有帮助。祝你好运

我已经试过了,虽然它确实让typescript关闭了,但它也破坏了任何类型检查:\n例如,如果为每种类型编写一个开关大小写,它将不再识别该大小写:“integer”将保证obj.integer作为属性。因此vs代码不提供代码完成等一些答案!你当然是对的,我的意思是整数是关键。我试着在操场上玩,看看什么东西在哪里断了,然后注意到绳子。类型ConfigFor=Extract似乎有点多余?首先将T分配给Extendes all Config['type'],然后将type分配给T。type ConfigFor不也是这样做的吗?我不明白您认为什么是多余的
type ConfigFor=…
是一个类型别名定义,因此我不能省略右侧
T
扩展
Config['type']
;它不一定等于
Config['type']
。例如,您可以为
T
传入
“text”
ConfigFor
的思想是获取union类型
Config
,并只提取具有
{type:T}
的union的组成部分。它是在类型层次上区分一个受歧视的联合体。
type Lookup<T, K> = K extends keyof T ? T[K] : never;
type ConfigFor<T extends Config['type']> = Extract<Config, { type: T }>;
function createConfig<T extends Config['type']>(
    type: T,
    value: Lookup<ConfigFor<T>, T>
) {
    const config = {
        type,
        [type]: value
    } as any as Config;
    appendToBody(config);
}