В TypeScript версии 2.2 был введён новый тип object. Он описывает любой непримитивный тип.
Следующие типы принято считать примитивными в JavaScript:


  • boolean
  • number
  • string
  • symbol
  • null
  • undefined

Все остальные типы принято считать непримитивными.


Новый тип object представляет именно их:


// All primitive types
type Primitive =
    | boolean
    | number
    | string
    | symbol
    | null
    | undefined;

// All non-primitive types
type NonPrimitive = object;

Давайте посмотрим, как object поможет нам более точно описывать типы.


Описания типов, использующие тип object


С релизом TypeScript версии 2.2 описания типов стандартной библиотеки были обновлены с использованием нового типа object. Например, методы Object.create() и Object.setPrototypeOf() теперь описывают параметр прототипа, как object | null:


interface ObjectConstructor {
    /**
      * Creates an object that has the specified prototype or that has null prototype.
      * @param o Object to use as a prototype. May be null.
      */
    create(o: object | null): any;

    /**
      * Sets the prototype of a specified object o to  object proto or null. Returns the object o.
      * @param o The object to change its prototype.
      * @param proto The value of the new prototype or null.
      */
    setPrototypeOf(o: any, proto: object | null): any;

    // ...
}

Если передать примитивный тип, в качестве прототипа в Object.setPrototypeOf() или в Object.create(), то, во время исполнения кода, будет выброшено исключение TypeError. Теперь TypeScript отлавливает такие ошибки на этапе компиляции:


const proto = {};

Object.create(proto);      // OK
Object.create(null);       // OK

Object.create(undefined);  // Error
Object.create(1337);       // Error
Object.create(true);       // Error
Object.create("oops");     // Error

Другим местом для применения типа object является структура данных WeakMap. Ключи которой должны быть объектами и не могут являться примитивами. Это требование нашло отражение в тайпингах:


interface WeakMap<K extends object, V> {
    delete(key: K): boolean;
    get(key: K): V | undefined;
    has(key: K): boolean;
    set(key: K, value: V): this;
}

object vs Object vs {}


Возможно Вас смутит, что TypeScript определяет несколько типов, имеющих похожие имена, но представляющие разные концепции:


  • object
  • Object
  • {}

Мы рассмотрели новый тип object выше. Теперь давайте обсудим, что же из себя представляют Object и {}.


Тип Object


Typescript определяет другой тип, с почти таким же названием, как и новый тип object, и это тип Object. В то время как object (с маленькой буквы) представляет все непримитивные типы, Object (с большой буквы) описывает функциональность, общую для все JavaScript объектов. Например, методы toString() и hasOwnProperty(). В файле lib.es6.d.ts тип Object определяется следующим образом:


interface Object {
    // ...

    /** Returns a string representation of an object. */
    toString(): string;

    /** Returns a date converted to a string using the current locale. */
    toLocaleString(): string;

    /** Returns the primitive value of the specified object. */
    valueOf(): Object;

    /**
      * Determines whether an object has a property with the specified name.
      * @param v A property name.
      */
    hasOwnProperty(v: string): boolean;

    /**
      * Determines whether an object exists in another object's prototype chain.
      * @param v Another object whose prototype chain is to be checked.
      */
    isPrototypeOf(v: Object): boolean;

    /**
      * Determines whether a specified property is enumerable.
      * @param v A property name.
      */
    propertyIsEnumerable(v: string): boolean;
}

Тип {}


Есть ещё один тип, который очень похож: {}, пустой тип объекта. Он описывает объект у которого нет собственных свойств. При попытке доступа к произвольным свойствам такого объекта TypeScript выдаёт ошибку во время компиляции:


// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "value";

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


// Type {}
const obj = {};

// "[object Object]"
obj.toString();

Комментарии (18)


  1. vanxant
    16.01.2018 03:54

    Мммм, я правильно понимаю, что построили сначала строго-типизированную надстройку над мерзко-утино-почтине-типизированным javascript, а потом, через N лет, это всё решили отменить?)


    1. Shtucer
      16.01.2018 04:19

      Нет. Это было сделано раньше. Тип any был с самого сразу. Иначе, как бы оно дружило с JS?


      1. vanxant
        16.01.2018 05:22

        Нет, ну any это any. Заглушка в стиле «возвращает то, не знаю что». А object это именно объект, просто с утиной типизацией (т.е. требования к наличию определённых свойств в этом объекте определяются нижележащим кодом).


        1. Shtucer
          16.01.2018 08:45

          Ну, как бы any, как мне кажется, делает "строго-типизированную надстройку" не такой уж и строгой прям с фундамента. Заглушка any не только про "возвращает то, не знаю что". Ее можно пихать во все места и не заморачивать мозг всеми этими типизациями. С object уже не то манто. Как, повторюсь, мне кажется.


        1. eliduvid
          16.01.2018 17:22

          На сколько я понял из статьи это тоже заглушка типа any, которая только проверяет на не примитвность. Это не значит, что можно обращаться к любым свойствам без приведения.


          1. PYXRU
            16.01.2018 19:55

            Благодаря этой заглушки они усилили типизацию как раз, потому что теперь если метод принципе не может принять примитив он не будет допущен ts компилятором. any — такое бы позволил.


      1. alex6636
        16.01.2018 19:49
        -2

        Что не отменяется мерзости js


    1. mayorovp
      16.01.2018 09:23

      Нет, они ввели новый тип для того чтобы более точно описать некоторые существующие интерфейсы. Например, тот же Object.create. Каким образом уточнение модели ее вдруг отменяет?


  1. DarkByte2015
    16.01.2018 17:21

    Вообще это нормально, но они зря его назвали также как существующий Object. Будет большая путаница несмотря на все объяснения. Разница в регистре одной буквы это не о чем. Вообще это очень напоминает C#. Там есть классы (class) и структуры (struct). Под структурой понимается любой ValueType (числа, булевые значения, в общем примитивные типы), а под классами как раз — классы (то что тут назвали object). Также есть корень иерархии — тип System.Object (можно с маленькой буквы object — алиас просто). Короче я о том что надо было поднапрячься и все же придумать имя получше…


    1. tenbits
      17.01.2018 03:07

      На самом деле, такое наименование следует тому же принципи что и Number:number, String:string, Boolean:boolean. С этим же ни у кого проблем не возникает.


      1. DarkByte2015
        17.01.2018 09:19

        Тогда это скорее как в Java. Там тоже есть примитивный int, а есть объект Integer.


        1. mayorovp
          17.01.2018 09:57

          Именно так и есть. Точно так как и в Java, Number — это упакованная версия number.


          Разве что вот теперь Object и object отличаются не упакованностью, а занимают разные места в иерархии наследования...


  1. FlamyXD
    16.01.2018 17:22

    Разве null и undefined являются типами? А не значениями?


    1. SayLovePlz Автор
      16.01.2018 17:22

      1. FlamyXD
        16.01.2018 17:34

        Там написано, что это «Специальное значение».


        1. DarkByte2015
          16.01.2018 17:52
          +1

          Значение null не относится ни к одному из типов выше, а образует свой отдельный тип, состоящий из единственного значения null


          1. FlamyXD
            16.01.2018 19:58
            -1

            Это просто специальное значение, которое имеет смысл «ничего» или «значение неизвестно».


            1. Vadem
              17.01.2018 17:12
              +1

              Строго говоря, стандарт определяет именно отдельные типы для null и undefined:
              ECMAScript Language Types
              И у них ровно по одному значению.