TypeScript 3.x: доступ к свойствам неизвестного типа

Я прочитал из документации TypeScript, что вы не можете получить доступ к свойствам из неизвестного типа:

// No property accesses, element accesses, or function calls

function f11(x: unknown) {
    x.foo;  // Error
    x[5];  // Error
    x();  // Error
    new x();  // Error
}

Но я не понимаю почему? Если я могу присвоить каждое значение, включая объект, почему я не могу получить доступ к свойствам?

Я борюсь со следующим сценарием: у меня есть объект типа any, я передаю его методу Object.entries, а затем вызываю forEach.

Поскольку он имеет тип any, аргумент вызова forEach будет массивом с первым элементом типа string, а вторым - с типом unknown.

В моем случае этот второй элемент является объектом, но я не могу получить доступ к его свойствам без преобразования его типа, что кажется неправильным.

Вот пример, который выдает ошибку TS (просто абстракция, я знаю, что в этом случае нет смысла объявлять ее как любую):

const obj: any = {
  val1: "someval",
  val2: {
    some: "some",
    object: "object",
  },
};
Object.entries(obj).forEach(el => {
  if (el[1].some) {
    console.log(el);
  }
});

предварительный просмотр ошибок

Я полагаю, это также может быть неправильный ввод для метода Object.entries, но я все же хотел бы объяснить, почему я не могу получить доступ к свойствам типа unknown.

Итак, вкратце мои вопросы:

  1. Почему я не могу получить доступ к свойствам типа unknown, даже если тип unknown может быть объектом?
  2. Я думаю, что есть причина для вышеуказанного вопроса, но Object.entries не должен возвращать массив с номером элемента. 0 строки типа и номер элемента. 1 типа какой-нибудь?

person josias    schedule 21.01.2020    source источник


Ответы (3)


Я думаю, что для ответа на ваш вопрос важно дать некоторый контекст по any vs unknown. Хотя вы можете найти исчерпывающий список сравнения в официальная документация по TypeScript, думаю, я могу позволить себе немного свободы и сократить статью до нескольких утверждений: any в основном универсален и поэтому небезопасен по типу . Под типобезопасностью я подразумеваю, что вы можете получить доступ к несуществующему свойству времени выполнения any.

unknown другое. unknown является противоположностью any в этом отношении. Он представляет собой типобезопасную версию any, заявив: «Я не буду притворяться, что подхожу ко всему, потому что я этого не делаю». Итак, unknown требует дополнительного приведения к желаемому типу, чтобы работать (потому что сам по себе не обладает никакими свойствами).

Теперь к собственному вопросу. Почему Object.entries использовать unknown вместо any? Поскольку безопаснее сказать «преобразовать это неизвестное значение во все, что вам нужно, перед использованием, я не буду предполагать, что я что-то знаю об этом типе», чем «я не знаю, какого типа он есть, но я предполагаю, что он имеет все возможные свойства всех возможных типов ".

person Alexey Kureev    schedule 21.01.2020
comment
О, значит, тип unknown действительно предназначен для переназначения типу с доступными свойствами? Таким образом, разработчику напоминают, когда он делает что-то вроде доступа к свойству, он должен быть уверен, что оно существует, чтобы не допустить ошибки, которая может вызвать ошибку времени выполнения. В то время как с типом any, он мог просто сделать это, что потенциально неверно и, следовательно, небезопасно с точки зрения типов. - person josias; 21.01.2020

У меня такая же проблема, но я пытаюсь ее решить с помощью этой техники

// choice is a custom data type 
export interface Choice {
  id: string; 
  isSelected: boolean; 
  score: number;
  status: string; 
}
for (const [key, value] of Object.entries(this.tableForm.value)) {
      if (value) {
        let choiceItem  = value as Choice;
        let choiceIds = [String(choiceItem.id)];
         
      }
    }
person Elbaz    schedule 29.06.2021

Определение Object.entries(...) (взято из GitHub) это:

entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];

Поскольку вы явно определили obj как any, вызов Object.entries возвращает

[string, unknown][]

Вы можете просто удалить : any при объявлении obj, и TypeScript определит его тип из определения.

В этом случае вызов Object.entries вернет:

[string, string | { some: string; object string; }][]

Чтобы ответить на ваши вопросы:

Почему я не могу получить доступ к свойствам типа unknown?

Компилятор пытается избежать ошибок времени выполнения, проверяя во время компиляции. Если вы действительно знаете, что делаете, вы всегда можете преобразовать unknown в any. Например:

const value:any = el[1];

Я предполагаю, что есть причина для вышеуказанного вопроса, но не должно ли Object.entries возвращать массив с номером элемента. 0 строки типа и номер элемента. 1 типа какой-нибудь?

Фактически он возвращает [string, unknown][].

person JC Olivares    schedule 21.01.2020
comment
Я понимаю, что в моем примере нет смысла объявлять это каким-либо образом. Это просто абстракция. i.E, когда объект поступает из выборки, он будет иметь тип any ... - person josias; 21.01.2020
comment
Я добавил резюме к своему вопросу. (Я знаю много способов решения моей проблемы, я просто не знаю, почему она существует) - person josias; 21.01.2020
comment
Я ценю ваши обновленные ответы, но они оба просто говорят, что это так, и это происходит, но я на самом деле спросил почему так и почему возвращен тип неизвестен, хотя с моей точки зрения any было бы точнее - person josias; 21.01.2020