Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.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
TypeScript从不推断类型,但需要赋值_Typescript_Type Inference_Conditional Types - Fatal编程技术网

TypeScript从不推断类型,但需要赋值

TypeScript从不推断类型,但需要赋值,typescript,type-inference,conditional-types,Typescript,Type Inference,Conditional Types,在我的项目中,有一个类充当文件的泛型类型。根据我们处理的文件类型,它应该公开其他属性 我尝试使用默认为never的条件类型来“隐藏”属性。但是,当我尝试使用该类时,类型检查器会抱怨我缺少已推断为never类型的属性。当然,我不能分配它,所以我留下了一个无法创建的对象 错误始终发生在这段代码的底部: // just for convenience type MP4OptionsT = { codec?: 'h264', profile: 'baseline' | 'main' |

在我的项目中,有一个类充当文件的泛型类型。根据我们处理的文件类型,它应该公开其他属性

我尝试使用默认为
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...
});
//只是为了方便
类型MP4OptionsT={
编解码器?:“h264”,
简介:“基线”|“主”|“高”,
比特率:数字,
};
//这是正在讨论的课程
类媒体文件{
公共路径:字符串;
公共格式:格式扩展“mp4”?“mp4”:格式扩展“png”?“png”:从不;//设置泛型类型参数后,它只能是特定的字符串文字
//如果泛型类型参数为“png”,则不必指定此参数
公共mp4选项:格式扩展“mp4”?mp4选项ST:从不;
建造商(选项:{
路径:字符串,
格式:格式扩展“mp4”?“mp4”:格式扩展“png”?“png”:从不;
//如果泛型类型参数为“png”,则不必指定该参数,但需要指定该参数
mp4Options:格式扩展了“mp4”?mp4Options ST:从不,
}) {
this.path=opts.path;
this.format=opts.format;
this.mp4Options=opts.mp4Options;
}
}
//这没关系
const mp4File=新媒体文件({
路径:'/some/file/somewhere.mp4',
格式:'mp4',
MP4选项:{
简介:"高",,
比特率:1000,
}
});
//类型检查器对此抱怨:“类型{…}中缺少属性mp4Otions”。
//如果我显式地包含mp4Options,类型检查器会注意到“type any不可分配给type never”——这是有意义的,但会阻止该类被实例化。
const pngFile=新媒体文件({
路径:'/some/file/somewhere.png',
格式:'png',//因为这个选项只有一个,如果隐式设置的话会更好。。。
});
根据我对本页条件类型部分的理解,mp4Options在被评估为
从不
类型后,似乎应该能够“不存在”。
作为ab实验,我也试着让它回到未定义的状态。如果我手动指定了
mp4Options: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
//只是为了方便
类型MP4OptionsT={
编解码器?:“h264”,
简介:“基线”|“主”|“高”,
比特率:数字,
};
类型FormatOptions=(F扩展了“mp4”?{mp4Options:MP4OptionsT}:{mp4Options?:never})
类媒体文件{
公共路径:字符串;
公共格式:format//此处不需要条件类型,它与format的类型相同
公共mp4Options:FormatOptions['mp4Options'];
建造商(选项:{
路径:字符串,
格式:格式,
}格式选项(&F)
{
this.path=opts.path;
this.format=opts.format;
this.mp4Options=opts.mp4Options;
}
}
//这没关系,不需要显式类型参数
const mp4File=新媒体文件({
路径:'/some/file/somewhere.mp4',
格式:'mp4',
MP4选项:{
简介:"高",,
比特率:1000,
}
});
mp4File.mp4Options.bitrate//ok
//不需要类型参数
const pngFile=新媒体文件({
路径:'/some/file/somewhere.png',
格式:'png',//不需要mp4Options
});
pngFile.mp4Options.codec//错误
这不是对我问题的直接回答,而是试图编写一个更具可读性的解决方案。提香·塞尔尼科娃·德拉戈米尔(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',
        });
    }
}
type LegalFormatT='mp4'|'png'|'jpg';
类型FormatOptions=F扩展“mp4”?{options:MP4OptionsT}:F扩展了“png”?{options:pngooptionst}:{options?:never};
类型MP4OptionsT={
编解码器?:“h264”,
简介:“基线”|“主”|“高”,
比特率:数字,
};
类型PNGOptionsT={
序列:布尔,
};
类媒体文件{
公共路径:字符串;
公共格式:格式;
建造商(选项:{
路径:字符串,
格式:格式,
}) {
this.path=opts.path;
this.format=opts.format;
}
}
类MP4MediaFile扩展了MediaFile{
公共选项:格式选项[‘选项’];
建造商(选项:{
路径:字符串,
选项:MP4选项ST,
}) {
超级({
路径:opts.path,
格式:'mp4',
});
this.options=opts.options;
}
}
类PNGMediaFile扩展了媒体文件{
公共选项:格式选项[‘选项’];
建造商(选项:{
路径:字符串,
选项:PNGOptionsT,
}) {
超级({
路径:opts.path,
格式:“png”,
});
this.options=opts.options;
}
}
类JPGMediaFil