#Is This TypeScript Code Validating Enum Constraints Correctly?

19 messages · Page 1 of 1 (latest)

magic tendon
#

I've been working on a TypeScript project where I need to ensure certain constraints on enum values within an object structure. I've come up with the following code that uses mapped types and conditional types to achieve the validation. The code seems to work as intended and doesn't show any errors in my development environment (e.g., VS Code), but I'm not entirely sure if I've implemented it correctly.

The code enforces that the values in the small_companies array should only contain values present in the all_companies array. Can someone please review the code and let me know if it's correctly enforcing these constraints? Additionally, any suggestions for improvement or potential issues that I might have missed would be greatly appreciated.


enum Companies {
  Maruti = "Maruti",
  Audi = "Audi",
  Renault = "Renault",
}

type SmallCompanies<T extends Companies[]> = T[number];

interface CompaniesTypes {
  all_companies: Companies[];
  small_companies: SmallCompanies<CompaniesTypes["all_companies"]>[];
}

// This will cause a TypeScript error
const companies: CompaniesTypes = {
  all_companies: [Companies.Maruti, Companies.Audi],
  small_companies: [Companies.Renault],
};

// This will not cause any TypeScript errors
const validCompanies: CompaniesTypes = {
  all_companies: [Companies.Maruti, Companies.Audi, Companies.Renault],
  small_companies: [Companies.Maruti],
};
 

I've provided comments within the code to explain its purpose, but I'm particularly interested in knowing if the SmallCompanies type and the CompaniesTypes interface are being used correctly for the validation. Your expertise would be incredibly helpful in ensuring that this code is reliable and error-free for my project. Thank you in advance for your assistance!

elfin sphinx
#

multiple things

#

when you talk about "ensure certain constraints on enum values within an object structure", we are talking about compile time validation, not runtime; just making sure

#

but the reason your code is not working is because of how you refer to the type of all_companies

#

your wrote

interface CompaniesTypes {
    all_companies: Companies[];
    // some more stuff
}
#

so the type of all_companies (CompaniesTypes["all_companies"]) will always return Companies[]

#

because it's refering to the type

#

not to an object having that type

#

so small_companies: SmallCompanies<CompaniesTypes["all_companies"]>[]; will always return Companies[]

#

and you won't ever get an error like you expect to

#

if you want each object to have that type but be "unique", you need generics

#

like so

#
enum Companies {
    Maruti = "Maruti",
    Audi = "Audi",
    Renault = "Renault",
}

type SmallCompanies<T extends Companies[]> = T[number];

interface CompaniesTypes<T extends Companies[]> {
    all_companies: T;
    small_companies: SmallCompanies<T>[];
}

// This will cause a TypeScript error
const companies: CompaniesTypes<[Companies.Maruti, Companies.Audi]> = {
    all_companies: [Companies.Maruti, Companies.Audi],
    small_companies: [Companies.Renault],
};

// This will not cause any TypeScript errors
const validCompanies: CompaniesTypes<[Companies.Maruti, Companies.Audi, Companies.Renault]> = {
    all_companies: [Companies.Maruti, Companies.Audi, Companies.Renault],
    small_companies: [Companies.Maruti],
};
#

however you'll have to repeat the generic type, I don't think it can be infered

#

however, what you could do is check the type when using the object

#

like so

#

!ts

wet violetBOT
#
enum Companies {
    Maruti = "Maruti",
    Audi = "Audi",
    Renault = "Renault",
}

interface CompaniesTypes<T extends Companies, U extends T> {
    all_companies: ReadonlyArray<T>;
    small_companies: ReadonlyArray<U>;
}

const companies = {
    all_companies: [Companies.Maruti, Companies.Audi],
    small_companies: [Companies.Renault],
} as const;

const validCompanies = {
    all_companies: [Companies.Maruti, Companies.Audi, Companies.Renault],
    small_companies: [Companies.Maruti],
} as const;

declare function foo<T extends Companies = Companies, U extends T = T>(comanbies: CompaniesTypes<T, U>): void;

foo(companies);
//  ^^^^^^^^^
// Argument of type '{ readonly all_companies: readonly [Companies.Maruti, Companies.Audi]; readonly small_companies: readonly [Companies.Renault]; }' is not assignable to parameter of type 'CompaniesTypes<Companies.Maruti | Companies.Audi, Companies.Maruti | Companies.Audi>'.
//   Types of property 'small_companies' are incompatible.
//     Type 'readonly [Companies.Renault]' is not assignable to type 'readonly (Companies.Maruti | Companies.Audi)[]'.
//       Type 'Companies.Renault' is not assignable to type 'Companies.Maruti | Companies.Audi'.
foo(validCompanies);
elfin sphinx
#

@magic tendon