Washington Guedes
Washington Guedes

Reputation: 4365

How to set the correct type as one of the defined object keys?

I've built this snippet to demonstrate my question:

const typeEnum = {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
};


const isCD = (...types: any[]) => {
    return types.some(t => {
        return t == typeEnum.c || t == typeEnum.d;
    });
};

console.log(
    isCD(
        typeEnum.a,
        typeEnum.b,
        typeEnum.d,
        6
    )
);

So what I want is to replace any[] to the correct type, but I don't know how to set that the acceptable values are the ones from typeEnum keys, so no one would pass 6 as argument because it is not an acceptable argument.

TypeScript Playground link

Thanks in advance.


My final working snippet ended this way:

const typeEnum = {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
} as const;

type typeEnumValues = typeof typeEnum [keyof typeof typeEnum];

const isCD = (...types: typeEnumValues[]) => {
    return types.some(t => {
        return t == typeEnum.c || t == typeEnum.d;
    });
};

console.log(
    isCD(
        typeEnum.a,
        typeEnum.b,
        typeEnum.d,
        6
    )
);

TypeScript Playground link

Upvotes: 2

Views: 108

Answers (2)

jcalz
jcalz

Reputation: 327744

For TS3.4+ you can use a const assertion to tell the compiler the type of typeEnum should be inferred to be as narrow as possible... a property value of 3 will be inferred as the numeric literal 3 and not as number:

const typeEnum = {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
} as const;
// inferred as type { readonly a: 1; readonly b: 2; readonly c: 3; 
//   readonly d: 4; readonly e: 5; }

If you haven't updated to TS3.4 yet you can get similar behavior with a helper function:

const litNumVals =
    <N extends number, T extends { [k: string]: N }>(t: T) => t;

const typeEnum = litNumVals({
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
});
// inferred as type { a: 1; b: 2; c: 3; d: 4; e: 5; }

From that value, you can use type functions to constrain isCD parameters:

type ValueOf<T> = T[keyof T]

type TypeEnumValues = ValueOf<typeof typeEnum>;
// 1 | 2 | 3 | 4 | 5    

const isCD = (...types: Array<TypeEnumValues>) => {
    return types.some(t => {
        return t == typeEnum.c || t == typeEnum.d;
    });
};

console.log(
    isCD(
        typeEnum.a, // okay
        typeEnum.b, // okay
        typeEnum.d, // okay
        6 // error
    )
);

Okay, hope that helps. Good luck!

Upvotes: 1

HTN
HTN

Reputation: 3594

You can't define a type from a const, but you can define the const 's type.

Is this solution OK for your use case?

type AcceptedValue = 1 | 2 | 3 | 4 | 5;
const typeEnum : {[key: string]: AcceptedValue} = {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5
};

Upvotes: 1

Related Questions