skip to Main Content

I have an interface like below

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;
}

when I use pick to pick one of this property like this

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

I got this error in this line of my code

        this.videoConstraints = videoConstraints;

Type  boolean | MediaTrackConstraints | undefined  is not assignable to type  Pick<CameraProps, "videoConstraints"> 
Type  undefined  is not assignable to type  Pick<CameraProps, "videoConstraints"> 

it seems pick remove undefined from my type but when I write the code like below

    private videoConstraints: CameraProps['videoConstraints'];

every thing is fine

The full code is like below

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

Answers


  1. That’s because Pick doesn’t do what you seem to believe it does. It returns a composite type with properties selected from another type. It doesn’t return a type of a single selected property.

    See this example (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']
    

    So

    Pick<CameraProps, ‘videoConstraints’>;

    evaluates to type

    { videoConstraints?: MediaStreamConstraints["video"] }
    

    but you already discovered that

    CameraProps['videoConstraints']
    

    is what you actually need. That will evaluate to

    MediaStreamConstraints["video"] | undefined
    
    Login or Signup to reply.
  2. In your type declaration, you make videoContraints optional with the ?.

    Pick constructs a new type from picking keys from another type, so you are essentially telling it to create this type with an optional property:

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

    When you assign a property to a different type which requires an optional property created by the Pick, it throws an error, whereas using the bracket notation is more lenient.

    To use the Pick type, you can use ! to assert it’s defined:

    this.videoConstraints = videoConstraints!;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search