4
type MatchOperator = "==" | ">" | "<";

type Criteria<T, P extends keyof T> = {
    field: P,
    value: T[P],
    operator: MatchOperator,
}

interface User {
    name: string;
    age: number;
    id: number;
}

const adultCriteria: Criteria<User, "age"> = {
    field: "age",
    operator: ">",
    value: 18
}

Is there a better way to restrict the type of value based on field using Typescript as mentioned below?

const adultCriteria: Criteria<User> = {
    field: "age",
    operator: ">",
    value: 18
}
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
Tamil
  • 5,260
  • 9
  • 40
  • 61
  • I think TypeScript typing concerns only types themselves. Values are something that is handled runtime, and type checks are handled during the compilation phase. They are two different problems. If you want to have an input validation for your incoming objects, you can use a library like falidator https://www.npmjs.com/package/@codeallnight/falidator - But if there is a good use case and way to do like you describe it in a question that would be interesting. – Mikko Ohtamaa Apr 28 '20 at 08:50
  • The type looks good. Best you could do is to try having the second type parameter inferred from the definition of the object. – Bergi Apr 28 '20 at 08:51

1 Answers1

5

Yeah, it's possible:

type Criteria<T> = {
    [P in keyof T]: {
        field: P,
        value: T[P],
        operator: MatchOperator
    }
}[keyof T]

This way you get a union type composite of 3 possible types:

type OnePossibleCriteria = Criteria<User>

type OnePossibleCriteria = {
    field: "name";
    value: string;
    operator: MatchOperator;
} | {
    field: "age";
    value: number;
    operator: MatchOperator;
} | {
    field: "id";
    value: number;
    operator: MatchOperator;
}

And when you assign a solid value to it, it's narrowed down to one of them.

const adultCriteria: OnePossibleCriteria = {
    field: "age",
    value: 18,
    operator: ">"
}

TypeScript Playground

hackape
  • 18,643
  • 2
  • 29
  • 57
  • 1
    That's super-intelligent :) – Tamil Apr 28 '20 at 08:58
  • Sure, before I wanna clear some doubts. Can you read the criteria type out for me? I'm not used to this `{}[keyof T]` . What does this mean? – Tamil Apr 28 '20 at 09:06
  • 1
    The thing inside braces `{ [P in keyof T]: something }` is a [mapped type](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types). It's basically a key-value pair struct, then you get the value part of that struct with indexing syntax `{}[keyof T]`, that yields a union type of all possible value. – hackape Apr 28 '20 at 09:40