logo
/
TypeScriptでオブジェクトの値を元に別の型を組み立てたい
2023-01-12

解決方法

satisfies operatorとreadonlyを指定すればいける
const columns = [
    { type: "numeric", name: "id" },
    { type: "text", name: "firstName" },
] as const satisfies readonly Column[]
type TypeOfColumns<T extends readonly Column[]> = {
    [K in T[number] as K["name"]]: K["type"] extends "numeric" 
      ? number 
      : string;
};

type ColumnsType = TypeOfColumns<typeof columns>;
/*
    type ColumnsType = {
        id: number;
        firstName: string;
    }
*/
Optionalもできる
type Column = { type: "numeric" | "text"; name: string; optional?: boolean };

const columns = [
	{ type: "numeric", name: "id" },
	{ type: "text", name: "firstName" },
	{ type: "text", name: "remark", optional: true },
] as const satisfies readonly Column[];

type IsOptional<T, V extends boolean | undefined> = V extends true
	? T | undefined
	: T;

type TypeOfColumns<T extends readonly Column[]> = {
	[K in T[number] as K["name"]]: K["type"] extends "numeric"
		? IsOptional<number, K["optional"]>
		: K["type"] extends string
		? IsOptional<string, K["optional"]>
		: unknown;
};

type ColumnsType = TypeOfColumns<typeof columns>;

/*
type ColumnsType = {  
	id: number;  
	firstName: string;  
	remark: string | undefined;  
}
*/

やりたいこと

こんな感じの単純な型情報の抜き出しじゃなくて
const some = {id: 1, firstName: 'tanaka'}
type Some = typeof some
オブジェクト定義の一部の値を使って新しい型情報を組み立てたい
type Column = Readonly<{ type: "numeric" | "text"; name: string }>;

type TypeOfColumns<T extends Column[]> = {}; // ...

const columns: Column[] = [
	{ type: "numeric", name: "id" },
	{ type: "text", name: "firstName" },
];

// If I wrote the following,
type ColumnType = TypeOfColumns<typeof columns>;

// I want the type to be inferred as follows,
type NeedColumnType = { id: number; firstName: string };
イメージとしてはzodzod.inferの用な感じで書きたい
import * as z from "zod";

const schema = z.object({
  str: z.string(),
  num: z.number()
})

// スキーマから型を生成!
type SchemaType = z.infer<typeof schema>;
/*
  {
    str: string
    num: string
  }
*/

逆方向もできるか試す

ChatGPTに聞いたら普通に答えてくれた(怖い)

質問内容

The following type definitions exist in Typescript.

```ts
type A = {
	id: number;
	firstName: string;
};
```

From the above type definitions, we would like to dynamically infer the following type definitions. Is it possible to create such a type definition helper?
```ts
type Columns = [
	{ type: "numeric"; name: "id" },
	{ type: "text"; name: "firstName" }
];
```

解決方法

違反なプロパティの抑制はできるが、推測される型定義が以下のように|なので、配列の数が足りなくてもエラーにならない。
type Columns = ({
    type: "numeric";
    name: "id";
} | {
    type: "text";
    name: "firstName";
} | {
    type: "text";
    name: "remask";
})[]
type TypeObject = {
	id: number;
	firstName: string;
	remask: string;
};

type ColumnsFromType<T> = {
	[K in keyof T]: {
		type: T[K] extends number
			? "numeric"
			: T[K] extends string
			? "text"
			: "unknown";
		name: K;
	};
}[keyof T][];

type Columns = ColumnsFromType<TypeObject>;

const columns: Columns = [
	{ type: "numeric", name: "id", source: [] },
	{ type: "text", name: "firstName", source: [] },
];

ログ