typescript - TypeScript 推断从不输入,但需要赋值
问题描述
在我的项目中,我有一个充当文件通用类型的类。根据我们正在处理的文件类型,它应该公开其他属性。
never
我尝试使用默认为“隐藏”属性的条件类型来实现这一点。但是,当我尝试使用该类时,类型检查器会抱怨我缺少被推断为 type 的属性never
。当然,我不能分配它,所以我留下了一个无法创建的对象。
错误发生在这段代码的底部:
// just for convenience
type MP4OptionsT = {
codec?: 'h264',
profile: 'baseline' | 'main' | 'high',
bitrate: number,
};
// this is the class in question
class MediaFile<Format extends 'mp4' | 'png'> {
public path: string;
public format: Format extends 'mp4' ? 'mp4' : Format extends 'png' ? 'png' : never; // once the generic type argument is set, this can only be a specific string literal
// this should not have to be assigned if generic type argument is 'png'
public mp4Options: Format extends 'mp4' ? MP4OptionsT : never;
constructor(opts: {
path: string,
format: Format extends 'mp4' ? 'mp4' : Format extends 'png' ? 'png' : never;
// this should not have to be assigned if generic type argument is 'png' - however it demands to be assigned
mp4Options: Format extends 'mp4' ? MP4OptionsT : never,
}) {
this.path = opts.path;
this.format = opts.format;
this.mp4Options = opts.mp4Options;
}
}
// this is OK
const mp4File = new MediaFile<'mp4'>({
path: '/some/file/somewhere.mp4',
format: 'mp4',
mp4Options: {
profile: 'high',
bitrate: 1000,
}
});
// the type checker complains about this: "Property mp4Otions is missing in type {...}".
// if I explicitly include mp4Options, the type checker notes that "Type any is not assignable to Type never" - which makes sense, but precludes this class from ever being instantiated.
const pngFile = new MediaFile<'png'>({
path: '/some/file/somewhere.png',
format: 'png', // since there is exactly one option for this, it would be nice if it were implicitly set...
});
根据我对本页条件类型部分http://www.typescriptlang.org/docs/handbook/advanced-types.html的理解,似乎 mp4Options 一旦被评估为应该能够“不存在”是类型never
。作为 ab 实验,我也尝试让它回退到未定义。如果我手动分配mp4Options: undefined
了这行得通,否则类型检查器仍然抱怨缺少属性。我认为绝对不应该是这种情况,因为我们可以省略undefined
开箱即用的属性(没有条件类型)。
是否有解决方法或不那么复杂的方法?还是我的代码中有错误?
解决方案
我认为使用通用基类为 and 派生两个单独的类可能会MediaFile
更好。mp4
png
如果您确实想通过有条件的魔术路线进入单节课,我们可以做到。尽管条件类型不能像您想要的那样影响属性的可选性,但我们可以将它们与交集类型结合以获得所需的效果:
// just for convenience
type MP4OptionsT = {
codec?: 'h264',
profile: 'baseline' | 'main' | 'high',
bitrate: number,
};
type FormatOptions<F extends 'mp4' | 'png'> = (F extends 'mp4' ? { mp4Options: MP4OptionsT } : { mp4Options?: never})
class MediaFile<Format extends 'mp4' | 'png'> {
public path: string;
public format: Format // no need for a conditional type here, it the same type as Format
public mp4Options: FormatOptions<Format>['mp4Options'];
constructor(opts: {
path: string,
format: Format,
} & FormatOptions<Format>)
{
this.path = opts.path;
this.format = opts.format;
this.mp4Options = opts.mp4Options;
}
}
// this is OK, no need for explicit type arguments
const mp4File = new MediaFile({
path: '/some/file/somewhere.mp4',
format: 'mp4',
mp4Options: {
profile: 'high',
bitrate: 1000,
}
});
mp4File.mp4Options.bitrate // ok
// no need for the type argument
const pngFile = new MediaFile({
path: '/some/file/somewhere.png',
format: 'png', // no need for mp4Options
});
pngFile.mp4Options.codec // error
推荐阅读
- docker - 在 Arch Linux 上安装 Docker - 来自 url 的 404 错误
- r - 当我保存对数据变量所做的更改时,正在保存旧状态。哪里错了?
- git - 如何保持 github 项目的分叉版本同步以及在提交更改时使用什么流程?
- python - 如何在 sqlalchemy 中长时间保持会话活动?
- html - 如何在我的网站上使用 css 转换按钮
- autodesk-forge - 伪造查看器与 Potree 的集成
- javascript - 从具有两个类的列中计算两个总和
- ionic-framework - 如何在离子中创建带有左右文本的切换按钮?
- php - 如何在 WooCommerce 中使用自定义订单状态更改订单状态
- javascript - 对加载更多项目时的自动滚动做出反应