Typescript: создать интерфейс или самый узкий тип из объекта - PullRequest
0 голосов
/ 06 февраля 2020

Есть ли способ создать интерфейс или узкий тип из объекта?

Например, я хотел бы что-то вроде:

const obj : someType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

type myType = narrowTypeOf obj

Тогда как myType должно привести к :

{
  a: string,
  b: string,
  c: {
    c: string
  }
};

К сожалению (или, к счастью, лучше), сборка в type mytype = typeof obj приведет к someType, так как я уже объявил, что это тот тип.

Если есть способ использовать объект в качестве параметра для определения типа - это решило бы и мою проблему.

1 Ответ

2 голосов
/ 06 февраля 2020

Если SomeType не является конкретно объединением , в котором один из членов является узким типом, который вы хотите, это не сработает. Давайте сначала посмотрим на счастливый случай, когда SomeType является таким объединением, хотя, очевидно, это не ваш случай использования:

type SomeType = {
  a: string,
  b: string,
  c: {
    c: string
  }
} | { foo: string } | { bar: string };

Здесь SomeType явно является объединением тип, который вы хотите, и два несвязанных типа. Затем, когда вы присваиваете значение obj, помеченное как SomeType,

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

, тип typeof obj, захваченный в этой точке, является тем типом, который вы хотите:

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

тип MyType не содержит {foo: string} или {bar: string}. Это связано с тем, что компилятор использует анализ типов на основе потока управления для сужения типов объединения при присваивании (см. microsoft / TypeScript # 8010 для комментариев PR, реализующих это).

Но при назначении нет сужения потока управления на основе несоединения типизированных переменных. В какой-то момент команда TS, похоже, считала , учитывая ее , и есть открытое предложение (см. microsoft / TypeScript # 16976 ) для сужения потока управления для типов, не являющихся объединением, но на данный момент это просто не часть языка.

Это означает, что если ваш SomeType шире, чем вы хотели, но не объединение, содержащее элемент, вы застряли. После того, как вы аннотируете переменную такого широкого типа, все присваивания этой переменной забудут что-либо более конкретное c о текущем значении этого типа.


Здесь нужно сделать не для аннотирования типа переменной. Единственная причина, по которой вы должны аннотировать переменную более широким типом, чем у присваиваемого вами значения, заключается в том, что вы хотите позднее изменить содержимое переменной, чтобы она стала другим обитателем этого более широкого типа. Например, если SomeType задано так:

interface SomeType {
  a: string | number;
  b: unknown;
  c: object;
}

Тогда единственная веская причина, по которой вы должны аннотировать следующее объявление как SomeType:

const obj: SomeType = {
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
};

, будет, если вы планировали сделать это позже:

obj.a = 123;
obj.b = () => 456;
obj.c = [789];

Если вы не планируете использовать этот более широкий тип, то вы должны позволить компилятору выводить типа obj. Если вы обеспокоены тем, что это позволит вам присвоить что-то несовместимое с SomeType, то вы можете использовать вспомогательную функцию, которая действует как сужающая аннотация:

const annotateAs = <T>() => <U extends T>(u: U) => u;

Этот помощник функцию можно использовать так:

const asSomeType = annotateAs<SomeType>();

Теперь asSomeType() - это функция, которая будет возвращать свой вход без расширения, но выдаст ошибку, если этот вход не может быть назначен на SomeType:

const oops = asSomeType({
  a: "a",
  b: "b",
  c: "c" // error! string is not an object
})

Теперь вы можете написать назначение obj следующим образом:

const obj = asSomeType({
  a: "a",
  b: "b",
  c: {
    c: "c"
  }
});

Это успешно, так что obj определенно является SomeType, но теперь тип obj это :

type MyType = typeof obj;
/* type MyType = {
    a: string;
    b: string;
    c: {
        c: string;
    };
} */

, как вы хотели.


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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...