reactjs - React TypeScript - Varför beter sig Boolean() annorlunda än double NOT (!!)-operatorn när man försöker rendera villkorligt med &&-operatorn?


När jag arbetar med JavaScript har jag alltid antagit att dubbel NOT-operatorn (!!) fungerar på samma sätt som Boolean(), när man försöker få ett sant/falskt värde från ett uttryck; dock, sedan jag började arbeta med TypeScript, stötte jag nyligen på ett scenario där de fungerade olika, och jag försöker förstå varför det är så. Ta följande kod som exempel:

import React from 'react'

interface Props {
    myNumber: number | null
}

const Test: React.FC<Props> = ({ myNumber }) => {
    return (
        <>
            {/* Example 1 */}
            {!!myNumber && <span>{myNumber.toString()}</span>}

            {/* Example 2 */}
            {Boolean(myNumber) && <span>{myNumber.toString()}</span>}
        </>
    )
}

export default Test

Under exempel #1 fungerar koden som jag förväntar mig: Om renderas betyder det att myNumber klarade !!-kontrollen, så det kan aldrig vara null, alltså kommer myNumber.toString() inte att ge ett fel.

Under exempel #2 kommer dock myNumber.toString() att returnera ett fel med meddelandet "Object is possibly null", även om denna kod endast borde nås om Boolean(myNumber) returnerar true, och Boolean(null) alltid borde vara false.

Har jag fel om jag tror att de borde bete sig exakt likadant? Finns det något relaterat till TypeScript-kompilatorn som jag missar? Tack!

Svar #1

Boolean är typad som en funktion av typen BooleanConstructor.

BooleanConstructor kan brytas ner ytterligare som <T>(value?: T | undefined) => boolean.

TypeScript tar inte hänsyn till en funktions interna arbete när det kontrollerar om ett värde kan vara null | undefined.

// Here TypeScript can directly infer that myNumber is indeed truthy 
// by the preceeding double bang (!!) check. 
const numberStrWithDoubleBang = !!myNumber && myNumber.toString();

// Here TypeScript cannot directly infer that myNumber is indeed truthy.
// To Typescript the function is a black box that takes in a number and
// returns a boolean, it does not know how the boolean value is calculated.
const numberStrWithFunction = isTruthy(myNumber) && myNumber.toString();

// While the isTruthy function above could easily have an implementation
// that does a simple truthy check on the passed in value as follows:
const isTruthy = <T>(value?: T | undefined) => !!value;

// It could also easily have an implementation with an output with no direct 
// connection to the truthy status of the passed in value such as:
const isTruthy = <T>(value?: T | undefined) => {
    const correctedValue = value ?? 0;
    const midPoint = correctedValue / 2;

    if (Math.random() * correctedValue > midPoint) {
        return true;
    }

    return false;
};

Vid denna tidpunkt anses inferens av null | undefined-tillstånd via en funktion vara för komplex eller att prestandapåverkan skulle bli för stor.

OBS: !!0 och Boolean(0) är båda falska eftersom 0 är ett falskt värde, om du behöver få strängrepresentationen av noll måste du uppdatera dina kontroller.

Relaterade artiklar