сделать опциональную опору в интерфейсе при реализации не обязательной - PullRequest
1 голос
/ 29 апреля 2020

предположим, что у нас есть интерфейс IFieldConfig

export interface IFieldInputConfig {
    type: 'string' | 'password';
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    default?: any;
}

export interface IFieldConfig extends IExtractConfig {
    id: string;
    form?: IFieldInputConfig;
}

и есть объект, который использует этот интерфейс

export const fieldCompany: IFieldConfig = {
    id: 'company',
    form: {
        type: 'string',
        default: 'Apple',
    },
}

, когда я хочу использовать его, TypeScript считает, что fieldCompany.form.default может быть неопределенным, даже если очень ясно, что оно определено. Я понимаю, что после объявления каким-то образом form prop можно было бы удалить, чтобы TypeScript не ошибался.

Как я могу сказать, что TypeScript запечатан / не будет изменен?

Очевидно, что я мог бы сделать больше специфических c интерфейсов, но я не хочу иметь один для каждой комбинации дополнительных опор. Я уверен, что есть более простой и простой способ

Ответы [ 4 ]

4 голосов
/ 29 апреля 2020

Возможно, я немного упрощен c, но одно из решений, которое выпадает, это просто не сказать, что это IFieldConfig вообще:

export const fieldCompany = {
  id: 'company',
  form: {
    type: 'string' as const, // <=== Note
    default: 'Apple',
  },
};

Теперь fieldCompany.form.default - это тип string, а не string | undefined. Вам нужно as const для type: 'string', потому что в противном случае оно будет выведено как string, а не 'string' | 'password'.

Объект по-прежнему совместим с присвоением IFieldConfig; это работает:

const x: IFieldConfig = fieldCompany;

На детской площадке

2 голосов
/ 29 апреля 2020

Вообще говоря, я бы не стал аннотировать переменную с типом, если бы я не хотел компилятора "забыть" особенности присваиваемого значения. Только присваиваемые объединению значения будут сужаться при присваивании, а IFieldConfig сам по себе не является объединяющим типом. Поэтому, когда вы присваиваете значение переменной типа IFieldConfig, тип переменной не сужается с помощью анализа потока управления до чего-либо более определенного c. См. microsoft / TypeScript # 16976 и microsoft / TypeScript # 27706 для получения дополнительной информации.

Мое предложение здесь (без дополнительной информации, чтобы заставить меня поверить в обратное): пропустите аннотацию fieldCompany и используйте const утверждение , чтобы сделать выводимый тип как можно более узким:

const fieldCompany = {
  id: 'company',
  form: {
    type: 'string',
    default: 'Apple',
  },
} as const;

Вы можете настроить это, если хотите (например, , возможно, только type поле form должно быть as const), в зависимости от того, какие части структуры вы хотите разрешить изменять, а какие части должны оставаться узкими / фиксированными.

Поскольку тип TypeScript система структурная и не номинальная, это не должно помешать вам использовать fieldCompany в качестве IFieldConfig:

function acceptIFieldConfig(c: IFieldConfig) { }
acceptIFieldConfig(fieldCompany); // okay

Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

0 голосов
/ 29 апреля 2020

Вы можете определить шаблон c DeepRequired, чтобы получить что-то вроде этого:

  interface IExtractConfig{}

  export interface IFieldInputConfig<L extends any = any> {
    type: 'string' | 'password';
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    default?: L;
  }

  export interface IFieldConfig<L extends any = any> extends IExtractConfig {
    id: string;
    form?: IFieldInputConfig<L>;
  }

  type DeepRequired<T> = Required<{
    [K in keyof T]: Required<DeepRequired<T[K]>>
  }>

  export const fieldCompany: DeepRequired<IFieldConfig<string>> = {
    id: 'company',
    form: {
        type: 'string',
        default: 'Apple',
    },
  }

Typescript не будет удовлетворен по умолчанию: любое поле. поэтому я изменяю его на универсальный c параметр.

Теперь у вас есть fieldCompany.form.default имеет "string"

  function user(fc:DeepRequired<IFieldConfig<string>>){
    //here         fc.form.default assumed to be "string" and not "string" | "undefined" 
  }

Некоторое время у вас должен быть базовый интерфейс с дополнительными свойствами. Но из моего опыта лучше использовать все свойства как обязательные. Позднее вы можете использовать Partial и Pick, чтобы разрезать оригинальную форму на куски.

0 голосов
/ 29 апреля 2020

Я попробовал ваш код на игровой площадке, и он действительно жалуется на возможность undefined.

Но к вашему спасению у вас есть некоторые обходные пути!

Оператор ненулевого подтверждения

fieldCompany.form!.default

Вы также можете отключить строгие проверки на ноль

tsconfig. json

{
  "compilerOptions": {
    "strictNullchecks": false
  }
}
...