代码之家  ›  专栏  ›  技术社区  ›  Ali Ehyaie

TypeScript中括号表示法属性访问和Pick实用程序之间的区别

  •  1
  • Ali Ehyaie  · 技术社区  · 2 年前

    我有一个如下的界面

    export type CameraProps = Omit<React.HTMLProps<HTMLVideoElement>, "ref"> & {
        audio?: boolean;
        audioConstraints?: MediaStreamConstraints["audio"];
        mirrored?: boolean;
        screenshotFormat?: "image/webp" | "image/png" | "image/jpeg";
        //between 0 & 1
        screenshotQuality?: number;
        videoConstraints?: MediaStreamConstraints["video"];
        fullScreenRecord?: boolean;
        screenshotDimensions?: ScreenshotDimensions;
        setImageSrc?: Dispatch<SetStateAction<string>>;
        overlay?: boolean;
    }

    当我使用pick来像这样选择一个属性时

        private videoConstraints: Pick<CameraProps, 'videoConstraints'>;
    

    我在代码的这一行中遇到了这个错误

            this.videoConstraints = videoConstraints;
    

    类型boolean | MediaTrackConstraints | undefined不可分配给类型Pick<CameraProps,“视频约束”> 类型undefined不可分配给类型Pick<CameraProps,“视频约束”>

    它似乎从我的类型中pick-remove-undefined,但当我写下面这样的代码时

        private videoConstraints: CameraProps['videoConstraints'];
    

    一切都很好

    完整代码如下

    import React, {Dispatch, SetStateAction} from "react";
    
    export type ScreenshotDimensions = {
        width: number;
        height: number;
    }
    
    export type CameraProps = Omit<React.HTMLProps<HTMLVideoElement>, "ref"> & {
        audio?: boolean;
        audioConstraints?: MediaStreamConstraints["audio"];
        mirrored?: boolean;
        screenshotFormat?: "image/webp" | "image/png" | "image/jpeg";
        //between 0 & 1
        screenshotQuality?: number;
        videoConstraints?: MediaStreamConstraints["video"];
        fullScreenRecord?: boolean;
        screenshotDimensions?: ScreenshotDimensions;
        setImageSrc?: Dispatch<SetStateAction<string>>;
        overlay?: boolean;
    }
    
    export type Maybe<T> = NonNullable<T> | undefined;
    
    export type VideoStatus = 'play' | 'error' | 'wait';
    
    
    class CameraManager {
        private setVideoStatus: Dispatch<SetStateAction<VideoStatus>>;
        private videoRef: MutableRefObject<HTMLVideoElement | null>;
        private audio: Maybe<boolean>;
        private videoConstraints: CameraProps['videoConstraints'];
        private audioConstraints: CameraProps['audioConstraints'];
        private fullScreenRecord: Maybe<boolean>;
    
        constructor({
                        audio,
                        setVideoStatus,
                        fullScreenRecord,
                        videoConstraints,
                        videoRef,
                        audioConstraints
                    }: Pick<CameraProps, 'audio' | 'fullScreenRecord' | 'videoConstraints' | 'audioConstraints'> & {
            setVideoStatus: Dispatch<SetStateAction<VideoStatus>>,
            videoRef: MutableRefObject<HTMLVideoElement | null>
        }) {
            this.setVideoStatus = setVideoStatus;
            this.videoRef = videoRef;
            this.fullScreenRecord = fullScreenRecord;
            this.videoConstraints = videoConstraints;
            this.audioConstraints = audioConstraints;
            this.audio = audio;
    
    
            this.requestUserMedia();
        }
    
        private async requestUserMedia() {
            const portrait = window.matchMedia("(orientation: portrait)").matches;
    
            try {
                this.setVideoStatus('play');
                const constraints: MediaStreamConstraints = {
                    video: this.fullScreenRecord ? {
                            aspectRatio: portrait ? document.documentElement.clientHeight / document.documentElement.clientWidth : document.documentElement.clientWidth / document.documentElement.clientHeight,
                            ...(typeof this.videoConstraints === 'object' ? this.videoConstraints : undefined),
                        } :
                        typeof this.videoConstraints === 'object' ? {
                            ...this.videoConstraints
                        } : true,
                };
    
                if (this.audio) {
                    constraints.audio = this.audioConstraints || true;
                }
                const stream = await navigator.mediaDevices.getUserMedia(constraints);
                if (!this.videoRef.current) {
                    return;
                }
                this.videoRef.current.srcObject = stream;
                return stream;
            } catch (e) {
                this.setVideoStatus('error');
                if (e instanceof Error) {
                    // Handle specific errors here
                }
                console.log(e);
            }
        }
    
    }
    2 回复  |  直到 2 年前
        1
  •  2
  •   j_s    2 年前

    那是因为 Pick 并没有做你认为会做的事。它返回一个具有从另一个类型中选择的属性的复合类型。它不返回单个选定属性的类型。

    参见此示例( playground ):

    type Stuff {
        a?: string
        b?: number
    }
    
    // The type is { a?: string | undefined }
    type T1 = Pick<Stuff, 'a'>
    
    // The type is string | undefined
    type T2 = Stuff['a']
    

    所以

    选取<CameraProps,“视频约束”>;

    计算为类型

    { videoConstraints?: MediaStreamConstraints["video"] }
    

    但你已经发现了

    CameraProps['videoConstraints']
    

    是你真正需要的。这将评估为

    MediaStreamConstraints["video"] | undefined
    
        2
  •  1
  •   inorganik    2 年前

    在类型声明中 videoContraints 可选,带有 ? .

    Pick 构造 从另一个类型中拾取键的类型,因此您实际上是在告诉它使用可选属性创建此类型:

    type SomeType = {
        videoConstraints?: MediaStreamConstraints["video"];
    }
    
    

    将特性指定给其他类型时 这需要 一个由Pick创建的可选属性,它会抛出一个错误,而使用括号表示法更为宽松。

    要使用“拾取”类型,可以使用 ! 断言其定义如下:

    this.videoConstraints = videoConstraints!;