#typescript #recursive-type
#typescript #рекурсивный тип
Вопрос:
Я пытаюсь написать универсальный тип, который принимает тип в качестве параметра (который может быть простым объектом, массивом, примитивом и т. Д.) И переназначает типы значений, когда это простой объект или массив, Чтобы добавить некоторые директивы конфигурации, описываемые Configuration
типом.
Давайте назовем этот гипотетический модификатор Configurable<T>
. T
может быть любым сложным вложенным объектом. Book
может быть значением T
, например :
type Configuration = {
$test: {
option1: boolean;
option2: string;
}
};
type Book = {
id: string;
title: string;
author: string;
related: Array<string>;
};
type Result = Configurable<Book>;
Затем я хочу Configurable<Book>
правильно проверить следующие выражения, в которых значения могут быть фактическими значениями или объектом конфигурации :
const expr1: Configurable<Book> = {
id: "1",
title: "Harry Potter",
author: "J.K. Rowling",
related: ["2", "3"]
}
const expr2: Configurable<Book> = {
id: "2",
title: "Harry Potter",
author: {
$test: {
option1: true,
option2: "something"
}
},
related: []
}
const expr3: Configurable<Book> = {
id: "3",
title: "Harry Potter",
author: "J.K. Rowling",
related: ["2", {
$test: {
option1: true,
option2: "something"
}
}]
}
const expr4: Configurable<Book> = {
id: "4",
title: true, // ERROR: should be string or Configuration
author: "J.K. Rowling",
related: ["2", "3"]
}
const expr5: Configurable<Book> = {
id: "5",
title: "Harry Potter",
author: "J.K. Rowling",
related: {
$test: {
option1: true,
option2: "something"
}
} // ERROR: should be an array of (string | Configuration)
}
Вложенный объект или массивы не должны заменяться на Configuration
, только там, где ожидается примитивное значение (см. expr5
).
Вот что я попробовал :
type Configuration = {
$test: {
option1: boolean;
option2: string;
};
};
type Configurable<T> = Record<string, any> extends T
? {
[K in keyof T]: Configurable<T[K]> | Configuration;
}
: T extends Array<infer U>
? Array<Configurable<U>>
: T;
Но это приводит expr2
к сбою и expr3
сбою.
Комментарии:
1. Не
expr2
требуетсяrelated
свойство? Я принимаю сумасшедшие таблетки здесь?2. @jcalz Да, моя ошибка.
Ответ №1:
Если я правильно понимаю ваши требования, вы могли бы использовать это определение Configurable
:
type Configurable<T> = T extends object ?
{ [K in keyof T]: Configurable<T[K]> } :
T | Configuration;
object
Тип соответствует любому не примитивному, включая массивы. Если T extends object
значение не равно true, то T
это примитив, который вы хотите принять T | Configuration
. Если T extends object
значение равно true, то вы сопоставляете его свойства с Configurable
помощью . Это должно автоматически выполнять правильные действия с массивами и кортежами, поскольку сопоставленные кортежи и массивы создают кортежи и массивы.
Давайте попробуем:
const expr1: Configurable<Book> = {
id: "1",
title: "Harry Potter",
author: "J.K. Rowling",
related: ["2", "3"]
}
const expr2: Configurable<Book> = {
id: "2",
title: "Harry Potter",
author: {
$test: {
option1: true,
option2: "something"
}
},
related: []
}
const expr3: Configurable<Book> = {
id: "3",
title: "Harry Potter",
author: "J.K. Rowling",
related: ["2", {
$test: {
option1: true,
option2: "something"
}
}]
}
Приведенные выше примеры компилируются без ошибок, как и требовалось. Давайте проверим ошибки:
const expr4: Configurable<Book> = {
id: "4",
title: true, // ERROR: should be string or Configuration
author: "J.K. Rowling",
related: ["2", "3"]
}
const expr5: Configurable<Book> = {
id: "5",
title: "Harry Potter",
author: "J.K. Rowling",
related: {
$test: {
option1: true,
option2: "something"
}
} // ERROR: should be an array of (string | Configuration)
}
Ошибки, которые вы хотите, действительно создаются. Выглядит хорошо!