首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么一个联合中的多个继承类型在类型副本泛型中不起作用?

为什么一个联合中的多个继承类型在类型副本泛型中不起作用?
EN

Stack Overflow用户
提问于 2022-07-11 14:59:32
回答 2查看 43关注 0票数 1

我有两种类型声明,一种接受字符串,另一种只将数字作为键。

testFunction不编译,说明MyStringKeyObject | MyNumberKeyObject类型的属性不存在。

有人能解释一下吗?

代码语言:javascript
复制
type MyStringKeyObject = {
  [key: string]: boolean;
};

type MyNumberKeyObject = {
  [key: number]: boolean;
};

const testFunction = <T extends MyStringKeyObject | MyNumberKeyObject>(
  generic: T
): void => {
  generic[1];
  generic.doesNotCompile;
};

操场链接

我正在使用的tsconfig

代码语言:javascript
复制
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-07-11 15:31:53

这种情况有点不寻常,因为您有一对索引类型,但接下来您将使用硬编码值对它们进行索引。通常,您会从对象中派生出密钥,这样就不会遇到这个问题。在您的情况下,这是因为代码中有显式的1doesNotExist

问题是,在编译时,generic可能是MyStringKeyObject MyNumberKeyObject.因此,TypeScript不知道要应用哪组规则,字符串键对象的规则或数字键对象的规则。

generic.doesNotExist的错误是非常有意义的:

属性'doesNotCompile‘不存在于'MyStringKeyObject \\ MyNumberKeyObject’类型上。另一种属性'doesNotCompile‘在'MyNumberKeyObject’类型中不存在。(2339)

该表达式对于MyStringKeyObject有效,但对MyNumberKeyObject无效,但generic可能是其中之一。

generic[1]的错误略有不同:

元素隐式具有'any‘类型,因为类型'1’的表达式不能用于索引类型'MyStringKeyObject \ MyNumberKeyObject‘。另一种属性'1‘不存在于'MyStringKeyObject \ MyNumberKeyObject’类型上。(7053)

我对generic[1]上的错误感到有点惊讶(但是,我仍然非常擅长TypeScript),因为如果您只是使用MyStringKeyObjectMyNumberKeyObject (而不是两者的结合),那么这两者都是有效的。(它允许使用字符串键对象,因为在JavaScript中,可以使用数字对任何对象进行“索引”,然后将数字转换为字符串。)尽管如此,您基本上还是遇到了与另一个问题相同的问题: TypeScript需要知道要应用哪些规则。

解决办法是找到一种方法来区分工会的一名成员和另一名成员,并相应地进行分支。但在运行时,您不能问对象是字符串索引对象还是数字索引对象。你有几个选择:

  1. 如果要在代码中使用这样的显式键,请为MyStringKeyObjectMyNumberKeyObject使用单独的函数。因为您无论如何都要分别处理它们(因为硬编码的键),这似乎是合乎逻辑的。
  2. 另一种方法是将编译时键添加到您可以用来区分它们的对象中;类型的联合,如果它们具有公共属性,可以将它们区分开来(“区分”它们),则称为受歧视的联合。

下面是#2的一个例子:

代码语言:javascript
复制
type MyStringKeyObject = {
    __type__: "string";
} & {
    [key: string]: boolean;
};

type MyNumberKeyObject = {
    __type__: "number";
    [key: number]: boolean;
};

const testFunction = <T extends MyStringKeyObject | MyNumberKeyObject>(generic: T): void => {
    if (generic.__type__ === "number") {
        generic[1];
    } else {
        generic.doesNotCompile; // But it does now. :-)
    }
};

操场链接

if/else的每个分支中,TypeScript都知道它所处理的类型( ifgeneric的类型缩小为联合的一个或另一个),因此您可以使用这些硬编码的属性名称。

票数 1
EN

Stack Overflow用户

发布于 2022-07-11 16:02:14

您的规则不允许您假设参数扩展了一种类型或另一种类型,因此您只能使用重叠的属性,在本例中没有重叠。(更正:从技术上讲,数字键是重叠的,因为它们被隐式转换为字符串)。

看起来您可能需要一个intersection而不是union

代码语言:javascript
复制
type MyStringKeyObject = {
  [key: string]: boolean;
};

type MyNumberKeyObject = {
  [key: number]: boolean;
};

const testFunction = <T extends MyStringKeyObject & MyNumberKeyObject>(
  generic: T
): void => {
  generic[1];
  generic['doesNotCompile'];
};

这是因为它说generic包含字符串键数字键。而您的则表示,它可能包含数字键字符串键。

对于一个联合,您需要在假设某个类型的键之前对参数进行类型检查。

您可以使用类与instanceof一起对参数进行类型检查。

代码语言:javascript
复制
class MyStringKeyObject {
  [key: string]: boolean;
}

class MyNumberKeyObject {
  [key: number]: boolean;
}

const testFunction = <T extends MyStringKeyObject | MyNumberKeyObject>(
  generic: T
): void => {
  if (generic instanceof MyNumberKeyObject) {
    console.log('number key', generic[1]);
  } else if (generic instanceof MyStringKeyObject) {
    console.log('string key', generic['string']);
  } else {
    console.log('Invalid parameter', generic);
  }
};

class NumberKeyExt extends MyNumberKeyObject {}
class StringKeyExt extends MyStringKeyObject {}

const numberKey = new MyNumberKeyObject();
const numberKeyExt = new NumberKeyExt();
const stringKey = new StringKeyExt();
const stringKeyExt = new MyStringKeyObject();
numberKey[1] = true;
numberKeyExt[1] = true;
stringKey['string'] = true;
stringKeyExt['string'] = true;
testFunction(numberKey); // number key true
testFunction(numberKeyExt); // number key true
testFunction(stringKey); // string key true
testFunction(stringKeyExt); // string key true

instanceof依赖于这样一个事实:对象是使用构造函数创建的。

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

https://stackoverflow.com/questions/72940589

复制
相关文章

相似问题

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