Машинопись: Исключить неопределенное из типа объединения

#typescript

Вопрос:

Существует пользовательский тип Options (вложенный объект) , который я создал. Я хочу использовать этот тип DefaultProperties для создания объекта, содержащего свойства по умолчанию.

 type Options = {
    id: number,
    text: string | undefined,
    properties: {
        title: string,
        subtitle: string | undefined,
    }
}

type DefaultProperties = Pick<Options, "properties">;
 

Конечно, свойства по умолчанию не могут быть undefined такими . Поэтому я хочу исключить undefined из своих типов союзов.
Для этой цели должны быть встроенные функции, такие как NonNullable<T> или Exclude<T,UnionType> .

Я попробовал три разных способа удаления undefined , но ни одно решение не работает с моим типом.

 type Options = {
    id: number,
    text: string | undefined,
    properties: {
        title: string,
        subtitle: string | undefined,
    }
}

type DefaultProperties = Pick<Options, "properties">;


// Try #1: does not work
type Type1 = NonNullable<DefaultProperties>;

// Try #2: does not work
type NotOptional<Type> = {
    [Property in keyof Type]-?: Type[Property];
};
type Type2 = NotOptional<DefaultProperties>;

// Try #3: does not work
type Type3 = Exclude<Options, undefined>;
 

Пример: Игровая площадка для машинописи

Как я могу удалить все undefined s из всех типов объединения типов?

Правка: Все undefined буквы s должны быть удалены из моего типа. Например:

  • Options['text'] : string (Больше нет undefined )
  • Options['properties']['subtitle'] : string (Больше нет undefined )

Позже я хочу использовать тип для этого (например):

 const options : Options = { ... };
const defaults: DefaultProperties= { ... }; // With no undefined

const node : HTMLElement = .... ;
node.innerHTML = options.properties.subtitle !== undefined ? options.properties.subtitle : defaults.properties.subtitle;
 

Когда defaults.properties.subtitle имеет тип string | undefined , компилятор выдает ошибку, потому innerHTML что только позволяет strings . За это я хочу исключить undefined .

Комментарии:

1. type NonNullable<T> = T extends undefined ? never: T;

2. Компилятор все еще говорит , что DefaultProperties['properties']['subtitle'] у него есть тип string | undefined , но я хочу, чтобы он был из типа string .

3. Попробуй text?: string

4. Ключ не должен быть необязательным.

Ответ №1:

Вы действительно близки к своему NotOptional типу утилиты , и Exclude проблема в том, что вы определяете DefaultProperties как объект со properties свойством , но пытаетесь удалить undefined его из DefaultProperties , а не из его properties свойства.

В комментарии вы сказали, что хотите DefaultProperties быть в той же форме, properties что и на Options . Это означает, что его основная форма (до того, как мы попытаемся удалить undefined ) такова Options["properties"] . Так что:

 type NotOptional<Type> = {
    [Property in keyof Type]: Exclude<Type[Property], undefined>;
};

type DefaultProperties = NotOptional<Options["properties"]>;
 

Тесты:

 const defaults1: DefaultProperties = {
    title: "foo",
    subtitle: undefined,    // <== Error as desired
};

const defaults2: DefaultProperties = {
    title: "foo",
    subtitle: "something",  // <== No error
};
 

Ссылка на игровую площадку

Вы также можете рассмотреть возможность устранения необязательности (объединение двух вещей, которые вы пробовали).:

 type NotOptional<Type> = {
    [Property in keyof Type]-?: Exclude<Type[Property], undefined>;
};

type DefaultProperties = NotOptional<Options["properties"]>;
 

Тесты:

 const defaults1: DefaultProperties = {
    title: "foo",
    subtitle: undefined,    // <== Error as desired (wrong type for `subtitle`)
};

const defaults2: DefaultProperties = {  // <== Error as desired (missing `somethingElse`)
    title: "foo",
    subtitle: "something",
};

const defaults3: DefaultProperties = {  // <== No error
    title: "foo",
    subtitle: "something",  // <== No error
    somethingElse: "x",
};
 

Ссылка на игровую площадку

Комментарии:

1. Я хочу, чтобы DefaultOptions она имела ту же форму, Options[properties] что и . При использовании pick properties также добавляется ключ. Не знаю, как его удалить, но решение с ключом properties работает для меня. @основная проблема: В обоих случаях компилятор возвращает string | undefined ключ subtitle . Это undefined то, что я хочу исключить. Например, используя его позже: document.querySelector('#n')!.innerHTML = options.properties.subtitle !== undefined : options.properties.subtitle : defaultOptions.properties.subtitle . Когда defaultOptions.properties.subtitle является string | undefined ошибкой.

2. @MichaelT — Извините, я уже два или три раза перепутал этот ответ. Я считаю, что текущая версия на самом деле делает то, что вы описали (что, кстати, не было неясно, я просто неправильно протестировал первую версию, а затем допустил ряд ошибок редактирования во второй версии).