Steven
Steven

Reputation: 1596

Typescript: Gather all string literals of a property of an array of objects

I'm trying to make something typesafe and I'm not 100% sure if it's possible:

If I have an array of "services", where a Service is defined as:

interface Service {
  id: string;
  dependencies?: [string] // refs to others Service.id's
}

Is there a way to make the dependencies array typesafe? To illustrate:

import { Service } from './service.ts';

const services: Service[] = [
  {
    id: "authors",
  },
  {
    id: "comments",
  }
  {
    id: "posts",
    dependencies: [] // <-- type this as `Array<"authors | "comments">`
  }
]

Upvotes: 1

Views: 216

Answers (2)

Stav Alfi
Stav Alfi

Reputation: 13933

A little bit ugly - to ensure that the id is listed in the array of the dependnecies, you can do the following:

type Id = 'id1' | 'id2' | 'id3'
type Obj<CurrentId extends Id> = {
  id: CurrentId
  dependencies: Exclude<Id, CurrentId>[]
}

const obj: <CurrentId extends Id>(obj: Obj<CurrentId>) => Obj<CurrentId> = obj => obj

const array = [
  obj({
    id: 'id1',
    dependencies: [], // compiled
  }),
  obj({
    id: 'id2',
    dependencies: ['id1'], // compiled
  }),
  obj({
    id: 'id3',
    dependencies: ['id3'], // compilation error
  }),
  obj({
    id: 'id3', // note: compiled even when there are multiple objects with the same Id
    dependencies: [],
  }),
]

Upvotes: 0

devdgehog
devdgehog

Reputation: 625

You can use unions to do something like this:

type RawType = "authors" | "comments";
type RawId = { id : RawType };
type Posts = {
  id: "posts";
  dependencies: RawType[];
}

type Service = RawId | Posts;

// then you can declare an array of Service
const services: Service[] = [
  {
    id: "authors",
  },
  {
    id: "comments",
  },
  {
    id: "posts",
    dependencies: [],
  }
];

Playground

Upvotes: 2

Related Questions