1

I'm currently working on the CVSS v3.1 implementation in TypeScript. Here is the specification. The most interesting part for my question is probably Table 15: Base, Temporal and Environmental Vectors. Let's have a look at the Environmental metric group. Here we have the metrics Confidentiality Requirement, Integrity Requirement, etc. Each metric has possible values, e.g. Not Defined(X), High(H), Medium(M), and Low(L). I thought that's a pretty good use case for enums and records.

I looked at all examples I could find online but most examples are pretty simple and their value type is always the same. My value type depends on the key.

Here is what I currently have. I tried to keep it simple and use only two metrics.

enum EnvironmentalMetric { ConfidentialityRequirement = 'CR', IntegrityRequirement = 'IR', } // references EnvironmentalMetric.ConfidentialityRequirement (CR) enum ConfidentialityRequirement { NotDefined = 'X', Low = 'L', Medium = 'M', High = 'H', } // references EnvironmentalMetric.IntegrityRequirement (IR) enum IntegrityRequirement { NotDefined = 'X', Low = 'L', Medium = 'M', High = 'H', } 

In the end I'd like to have two loops, an outer loop over all metrics and an inner loop for each metric value. So I tried to define a nested record. For every metric value I need to have some information like a textual description and a numeric value. This numeric value is required afterwards to calculate a score. So accessing this value in a type safe manner is also required.

interface Information { text: string value: number } // can I improve this? interface Metrics { text: string metrics: | Record<ConfidentialityRequirement, Information> | Record<IntegrityRequirement, Information> } const foo: Record<EnvironmentalMetric, Metrics> = { [EnvironmentalMetric.ConfidentialityRequirement]: { text: 'Confidentiality Requirement', metrics: { [ConfidentialityRequirement.NotDefined]: { text: 'Not Defined', value: 1, }, [ConfidentialityRequirement.Low]: { text: 'Low', value: 1 }, [ConfidentialityRequirement.Medium]: { text: 'Medium', value: 1 }, [ConfidentialityRequirement.High]: { text: 'High', value: 1 }, }, }, [EnvironmentalMetric.IntegrityRequirement]: { text: 'Integrity Requirement', metrics: { [IntegrityRequirement.NotDefined]: { text: 'Not Defined', value: 1 }, [IntegrityRequirement.Low]: { text: 'Low', value: 1 }, [IntegrityRequirement.Medium]: { text: 'Medium', value: 1 }, [IntegrityRequirement.High]: { text: 'High', value: 1 }, } } } // this should have type "Record<ConfidentialityRequirement, Information>" // but has "Record<ConfidentialityRequirement, Information> | Record<IntegrityRequirement, Information>" const a = better.CR.metrics // this should have type "Record<IntegrityRequirement, Information>" // but has "Record<ConfidentialityRequirement, Information> | Record<IntegrityRequirement, Information>" const b = better.IR.metrics 

My questions are:

  • Can I improve my Metrics interface so I don't have to use the union type?
  • Can I somehow make the "getters" type safe? So can I somehow say "whenever I get the value at key EnvironmentalMetric.ConfidentialityRequirement TypeScript knows it is of type Record<ConfidentialityRequirement, Information>".

I also tried using native Map, which works, but accessing values is quite annoying because every value can be undefined.

Here is a link to the playground.

Any ideas? Thank you very much!

1 Answer 1

2

We can using a mapped type an a map of one enum to the other:

type MetricToRequirement = { [EnvironmentalMetric.ConfidentialityRequirement]: ConfidentialityRequirement; [EnvironmentalMetric.IntegrityRequirement]: IntegrityRequirement; }; type Better = { [K in EnvironmentalMetric]: { text: string; metrics: Record<MetricToRequirement[K], Information>; }; }; 

You can liken this to a piece of (psuedo) code if it helps you understand:

let MetricToRequirement = { [EnvironmentalMetric.ConfidentialityRequirement]: ConfidentialityRequirement; [EnvironmentalMetric.IntegrityRequirement]: IntegrityRequirement; }; let obj = {}; for each member of EnvironmentalMetric: obj[K] = { text: string; metrics: Record<MetricToRequirement[member], Information>; } 

Playground

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.