简体   繁体   中英

Define TypeScript type based on generic type

Let's have SQL database table with column data of type JSON and this enum in TypeScript (4.0.5):

enum EContentType {
  NOTHING,
  IMAGE,
  VIDEO
}

Depending on ContentType I am saving different JSON schema into data column into my database.

Example:

  • For IMAGE , saving {"path": "/foo/bar", "type": "jpeg"}
  • For VIDEO , saving {"path": "/foo/bar", "length": 60}

Is possible to create TypeScript type for this data object with generics what will be correctly typed based on EContentType ?

Something like (pseudo):

type TDataType<ContentType> = {
   [ContentType.IMAGE]: {path: string, type: string}
   [ContentType.VIDEO]: {path: string, length: number}
}

Usage:

const concreteType: EContentType = EcontentType.IMAGE;

const typedDataField = dataField as TDataType<concreteType>;

Is this possible? Or it is not, because TypeScript is only statically typed... Any different idea how to guarantee type safety (not allow someone to save length property for IMAGE content type)?

If there is no way to do it what about type what will work like this:

const data1 = dbDataField as TDataType<EContentType.IMAGE> // {path: string, type: string}
const data2 = dbDataField as TDataType<EContentType.VIDEO> // {path: string, length: number}

This wouldn't work as you're declaring ContentType as a type but using it as a value. A better approach would be to use an interface and extends your desired properties on generic for anything you would want predefined

interface TDataType <T>{
   [key: string]: T
}

let a: TDataType<{path: string, type: string}>;
a.IMAGE = {path: 'aaa', type: 'bbbb'};
console.log(a); // output:  "IMAGE": {"path": "aaa", "type": "bbbb"}

or use The Record<Keys, type> utility type to define https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeystype

interface Content{
  path: string, type: string
}

type ContentType = "IMAGE" | "VIDEO";

const concreteType: Record<ContentType , Content> = {
  IMAGE: { path: "", type: ""}
};
type TDataType = {
  [key in EContentType]: {path: string, type: string}
}

type TDataType2<T> = {
  [key in keyof T]: { path: string; type: string };
};

You can use like this.


example)

enum EContentEnum {
  NOTHING,
  IMAGE,
  VIDEO,
}

type TDataType2<T> = {
  [key in keyof T]: { path: string; type: string };
};

const contentData: Pick<TDataType2<typeof EContentEnum>, 'IMAGE'> = {
  IMAGE: {
    path: 'imagePath',
    type: 'imageType',
  },
};

contentData.IMAGE

This is my answer, but I am not sure if you want it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM