TypeScript 版本: 3.8 & Nightly
搜索词:
- Omit
- Record
- Any
- 丢失类型信息
预期行为:
我希望以下类型按照内联注解描述。
type AnyRecord = Record<string, any>;
interface ExtendsAny extends AnyRecord {
myKey1: string;
myKey2: string;
}
// ExtendsAny['myKey1'] === string
// ExtendsAny['otherKey'] === any
type OmitsKey = Omit<ExtendsAny, "myKey2">;
// OmitsKey['myKey'] === string
// OmitsKey['otherKey'] === any
请注意,在不使用 omit 或不使用 record 的情况下,这可以正常工作,但当两者一起使用时,实际行为如下。
在使用 omit 之后,所有键都变成了 "any",即使特定定义的键存在,用于代码补全等的任何信息也会被删除,尽管它们在原始扩展中可以正常工作。
type AnyRecord = Record<string, any>;
interface ExtendsAny extends AnyRecord {
myKey1: string;
myKey2: string;
}
// ExtendsAny['myKey1'] === string
// ExtendsAny['otherKey'] === any
type OmitsKey = Omit<ExtendsAny, "myKey2">;
// 🚨Problem here 🚨
// OmitsKey['myKey1'] === any
// OmitsKey['otherKey'] === any
const instance: OmitsKey = {
myKey1: "test",
}
// 🚨 When typing this no autocomplete is provided for myKey1 🚨
instance.myKey1.
相关问题:
在此期间,我正在重新定义基本接口(非常大),因为它来自一个没有混合记录类型的库。我先执行 omit,然后再将其添加回来。
代码
/**
* @copyright 2020 Adam (charrondev) Charron
* @license MIT
*/
export interface IBase<T = number> {
baseProp: T;
baseProp2: T;
baseFunction: () => T;
}
const base: IBase<number> = {
baseProp: 0,
baseProp2: 0,
baseFunction: () => 0,
};
// number
base.baseProp;
///
/// Without the omit
///
type AnyRecord = Record<string, any>;
interface IExtend<T> extends IBase<T>, AnyRecord {
extendProp: T;
}
const extended: IExtend<number> = {
baseProp: 0,
baseProp2: 0,
baseFunction: () => 0,
extendProp: 0,
};
// number
extended.baseProp2;
///
/// With an Omit
///
interface IOmittedInterface extends Omit<IExtend<number>, "baseProp"> {}
const omitted: IOmittedInterface = {
baseProp2: 0,
baseFunction: () => 0,
};
// any
omitted.baseProp2;
输出
/**
* @copyright 2020 Adam (charrondev) Charron
* @license MIT
*/
const base = {
baseProp: 0,
baseProp2: 0,
baseFunction: () => 0,
};
// number
base.baseProp;
const extended = {
baseProp: 0,
baseProp2: 0,
baseFunction: () => 0,
extendProp: 0,
};
// number
extended.baseProp2;
const omitted = {
baseProp2: 0,
baseFunction: () => 0,
};
// any
omitted.baseProp2;
编译器选项
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"useDefineForClassFields": false,
"alwaysStrict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"downlevelIteration": false,
"noEmitHelpers": false,
"noLib": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"esModuleInterop": true,
"preserveConstEnums": false,
"removeComments": false,
"skipLibCheck": false,
"checkJs": false,
"allowJs": false,
"declaration": true,
"experimentalDecorators": false,
"emitDecoratorMetadata": false,
"target": "ES2017",
"module": "ESNext"
}
}
** playground 链接:**提供
8条答案
按热度按时间iugsix8n1#
也许我只是在这里显得有些愚钝,但是你声明了两个属性
myKey1
和myKey2
,然后引用了不存在的键myKey
,并期望从另一个不存在的键otherKey
中得到不同的行为?在我提到myKey1
的情况下,我看到了预期的行为。s8vozzvw2#
对不起,我的第一条示例中有一些拼写错误。第二条(从代码沙箱链接的)是正确的。
我已经更新了上面的示例,并将其放在这里。
问题在于,
myKey1
在原始接口中作为完全定义的键存在,但在使用Omit时会丢失其类型信息。kknvjkwl3#
我遇到了类似的问题
Line
this.g(v.value);
生成了一个错误我相信代码是正确的
rkue9o1l4#
我遇到了这个问题,编译器已经阻止它通过3天了。我刚刚意识到这是因为我的顶层接口扩展了Record<string, any>(TSC: 3.7.2):
klr1opcd5#
这是一个非常痛苦的问题。
我遇到了这个问题(注意:在这个简单的示例中,如果所有内容都放在一个文件中,问题就不会发生。当导入类型时,可能会丢失类型,可能只从npm注册表中丢失)。
vom3gejh6#
好的,我终于找到了Omit给我带来这么多麻烦的真正原因。
我有一个类型叫做
UnknownProps
,它看起来是这样的:我通过它来扩展我的接口,作为一种允许额外属性的方式,这些属性并没有严格定义
然后当我尝试将
ComponentProps
与Omit结合时,我没有得到任何类型,因为所有的摆脱
extends UnknownProps
解决了我的问题(尽管我更希望Omit
不要硬性失败,而是使用它已经访问到的已知键)这也是
keyof
和其他一些东西的问题。可能需要单独开一个问题...6tqwzwtp7#
@Dan503 你有没有找到一个解决方案,让你可以定义一些属性的类型,并且允许其他类型?这对我来说也是一个问题。
wkftcu5l8#
我最终将已知的属性保存到一个静态接口中,然后为组件属性创建一个单独的类型。
接下来,如果我想要对组件属性执行某些操作,我会针对静态已知的
StaticComponentProps
接口而不是动态的ComponentProps
类型进行操作。