![](/img/trans.png)
[英]TS infers "never" type because it can't grok assignment in forEach loop
[英]TypeScript infers never type but requires assignment
在我的項目中,我有一個類作為文件的通用類型。 根據我們處理的文件類型,它應該公開其他屬性。
我嘗試使用默認為never
“隱藏”屬性的條件類型來實現這一點。 但是,當我嘗試使用該類時,類型檢查器抱怨我缺少已推斷為類型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 實驗,我也嘗試讓它回退到 undefined。 如果我手動分配mp4Options: undefined
,這會起作用,否則類型檢查器仍然抱怨缺少屬性。 我認為絕對不應該是這種情況,因為我們可以省略開箱即用的undefined
屬性(沒有條件類型)。
是否有解決方法或不那么復雜的方法來做到這一點? 或者我只是在我的代碼中有錯誤?
我認為通過為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
這不是對我的問題的直接回答,而是嘗試編寫更具可讀性的解決方案。 Titian Cernicova-Dragomir 已經提供了一個很好的例子來說明如何做我最初要求的事情。
在玩了更多之后,我想出了這個解決方案,它避免了我在原始問題中詢問的復雜類型推斷:
type LegalFormatT = 'mp4' | 'png' | 'jpg';
type FormatOptions<F extends LegalFormatT> = F extends 'mp4' ? { options: MP4OptionsT } : F extends 'png' ? { options: PNGOptionsT } : { options?: never };
type MP4OptionsT = {
codec?: 'h264',
profile: 'baseline' | 'main' | 'high',
bitrate: number,
};
type PNGOptionsT = {
sequence: boolean,
};
class MediaFile<Format extends LegalFormatT> {
public path: string;
public format: Format;
constructor(opts: {
path: string,
format: Format,
}) {
this.path = opts.path;
this.format = opts.format;
}
}
class MP4MediaFile extends MediaFile<'mp4'> {
public options: FormatOptions<'mp4'>['options'];
constructor(opts: {
path: string,
options: MP4OptionsT,
}) {
super({
path: opts.path,
format: 'mp4',
});
this.options = opts.options;
}
}
class PNGMediaFile extends MediaFile<'png'> {
public options: FormatOptions<'png'>['options'];
constructor(opts: {
path: string,
options: PNGOptionsT,
}) {
super({
path: opts.path,
format: 'png',
});
this.options = opts.options;
}
}
class JPGMediaFile extends MediaFile<'jpg'> {
public options: FormatOptions<'jpg'>['options'];
constructor(opts: {
path: string,
}) {
super({
path: opts.path,
format: 'jpg',
});
}
}
雖然我真的很喜歡使用 TypeScript 提供的所有類型推斷功能,但我認為在這種情況下,最好“殺死我的寶貝”並做一些更多的手動工作,以避免讓一些未來的維護者感到恐慌。
非常感謝 Titian Cernicova-Dragomir 對實際問題的回答以及遵循“經典”擴展基類路線的動機。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.