Home > other >  Generic type function: Property does not exist
Generic type function: Property does not exist

Time:10-10

I'm creating a generic function logVisitorInfo<T>, with two fields. One is named type, defined as ("registeredUser" or "anonymous"). The second is named info, contains the information of the visitor (ip, username).

The generic function logVisitorInfo will type info to change based on the value of type.

But I am having a problem inside logVisitorInfo function:

type visitorType = {
    registeredUser: {
        ip: string;
        username: string;
    };
    anonymous: {
        ip: string;
    }
}
const logVisitorInfo = <T extends keyof visitorType>({ type, info }: {type: T, info: visitorType[T] }) => {
    const username = info.username; //error as expected, 'username' not exist on '{ ip: string; }'
   
    if(type === 'registeredUser') { //type is narrowed down to 'registeredUser', username should exists by now?
        const username = info.username; //but here still getting the same error
    }
}

It seems that type === 'registeredUser' doesn't do what I'm expecting. How can I avoid that error?

CodePudding user response:

TypeScript won't narrow info based on the type of type, which makes sense if you consider this application:

logVisitorInfo<'registeredUser' | 'anonymous'>({type: 'registeredUser', info: {ip: ''}})

Both type and info are correctly typed here, but info doesn't contain username.

To get the behavior you want, you can create a discriminated union type like this:

type Info = {[K in keyof VisitorType]: {type: K, info: VisitorType[K]}}[keyof VisitorType]
// type Info =
//  | { type: 'registeredUser'; info: { ip: string; username: string } }
//  | { type: 'anonymous'; info: { ip: string } }

Using Info, you can write logVisitorInfo without generics:

const logVisitorInfo = ({ type, info }:Info) => {
    const username = info.username; //error as expected, 'username' not exist on '{ ip: string; }'

    if(type === 'registeredUser') {
        const username = info.username;
    }
}

logVisitorInfo({type: 'anonymous', info: {ip: ''}}) // Ok
logVisitorInfo({type: 'registeredUser', info: {ip: ''}}) // Error

TypeScript playground

  • Related