Typescript: Sugerencia: el tipo primitivo `objeto`

Creado en 26 ene. 2015  ·  17Comentarios  ·  Fuente: microsoft/TypeScript

El tipo primitivo object

Esta propuesta describe un nuevo tipo primitivo para el mecanografiado object .

Caso de uso

La API central de JavaScript contiene algunas funciones que toman object como parámetro:

  • Object.getPrototypeOf
  • Object.getOwnPropertyDescriptor
  • Object.create
  • ... etc

Actualmente no hay forma en el mecanografiado de evitar pasar otro tipo primitivo ( string , number , etc ...) a esas funciones.
Por ejemplo, Object.create('string') es una declaración mecanografiada perfectamente válida, incluso si termina con un error.
La creación de un nuevo tipo primitivo object permitiría modelar más correctamente la firma de esas funciones y todas las funciones que comparten una restricción similar.

Comprobación de tipo

Asignabilidad

El tipo object es el equivalente a {} menos la asignabilidad de otro tipo básico, eso significa que:

  • cualquier otro tipo básico no se puede asignar a object
  • cualquier tipo no básico se puede asignar a object
  • el objeto solo se puede asignar a {} y any

Este comportamiento es coherente con el funcionamiento de javascript, el tipo representa cada valor que respeta la restricción typeof value === 'object' más undefined .

Tipo de guardia

Se debe introducir un nuevo tipo de protección por object : typeof value === 'object' .
Opcionalmente, el compilador también podría aceptar la comparación con la función Object emitida: Object(value) === value .

Suggestion help wanted

Comentario más útil

+1 en esta propuesta. No consideraría a WeakMap una API "experta", pero lo considero razón suficiente para implementar esto. Dado que el tiempo de ejecución hace una distinción entre tipos de objetos y tipos primitivos, solo tiene sentido tener una función en TypeScript para hacer la distinción también.

Esta propuesta también resolverá el problema de escribir "bolsas de objetos" donde cada propiedad es opcional, pero otros tipos primitivos no deberían permitirse.

Todos 17 comentarios

Sería útil enumerar algunas API distintas de las funciones Object. que se beneficiarían de esto.

En la api del núcleo de javascript, excepto en Object solo quizás algunas construcciones de es6 se beneficien de esto (WeakMap, Proxy, etc.).
Pero, por ejemplo, cada función de colecciones de subrayado también obtendría una mejor escritura, un marco como React usa un método 'setState' que acepta solo objetos o null , ImmutableJS, Mori, etc.

Editar: subrayar el trabajo de colección con cualquier tipo

Discutido en la reunión de revisión de sugerencias. Todo esto tiene sentido, pero necesitamos justificar la complejidad de un nuevo tipo primitivo con el número de errores / riqueza de descripción que puede proporcionar. Básicamente, más ejemplos de API que no pueden operar en primitivas, especialmente aquellas que no son API "expertas" (por ejemplo, Proxies).

Publicando más como referencia que recomendación: guiño:

interface String { _primitiveBrand?: string; }
interface Boolean { _primitiveBrand?: boolean; }
interface Number { _primitiveBrand?: number; }

interface ObjectOnly {  _primitiveBrand?: void; }

function fn(x: ObjectOnly) { }

// Error
fn(43);
// Error
fn('foo');
// OK
fn({});
// OK
fn(window);

+1 en esta propuesta. No consideraría a WeakMap una API "experta", pero lo considero razón suficiente para implementar esto. Dado que el tiempo de ejecución hace una distinción entre tipos de objetos y tipos primitivos, solo tiene sentido tener una función en TypeScript para hacer la distinción también.

Esta propuesta también resolverá el problema de escribir "bolsas de objetos" donde cada propiedad es opcional, pero otros tipos primitivos no deberían permitirse.

Object.observe requiere un objetivo no primitivo: http://arv.github.io/ecmascript-object-observe/#Object.observe

Creo que el título es un poco confuso ya que menciona la palabra "primitivo". Esto podría denominarse alternativamente _ "tipo de objeto ECMAScript" _ o _ "tipo de objeto de tiempo de ejecución" _. Que sería asignable desde cualquier otro tipo que no sean tipos primitivos (incluidos undefined tipo y símbolos), tipos de función o constructor. Los criterios para lo que es un tipo object legal deben basarse en la siguiente protección:

let isObject (x) => typeof x === "object";

Según MDN , esta es la salida de typeof :

Indefinido : "indefinido"
Nulo : "objeto" (ver más abajo)
Booleano : "booleano"
Número : "número"
Cadena : "cadena"
Símbolo (nuevo en ECMAScript 2015) : "símbolo"
Objeto de host (proporcionado por el entorno JS) : dependiente de la implementación
Objeto de función (implementa [[Call]] en términos de ECMA-262) : "función"
Cualquier otro objeto : "objeto"

Sin este tipo, no hay forma de hacer coincidir correctamente un tipo preciso para la protección mencionada anteriormente, ni siquiera como protección definida por el usuario. Esto también es esencial para usar con cualquier función que requiera estrictamente un objeto que tenga propiedades y permita for in bucles y llamadas al prototipo Object , y para implementar restricciones genéricas como T extends object y luego hacer referencia a propiedades de forma segura o usar las funciones de prototipo.

Sin embargo, tener funciones y constructores excluidos sería un poco limitante en algunos casos, por lo que podría haber otro tipo que los incluya también y se denomine _ "tipo de objeto no primitivo" _. Una solución parcial podría ser la unión object | Function aunque se necesitarían yesos o protectores adicionales para manejarlo correctamente.

Aceptando RP. Esto parece ser un problema común; este será un cambio rotundo para cualquiera lo suficientemente valiente como para haber escrito interface object { ... } . La implementación debe ser sencilla y seguir las reglas descritas anteriormente.

Nota al margen : ¡Todos nos preguntamos dónde ha estado

@RyanCavanaugh Cambio de ciudad de trabajo, y desafortunadamente no mucho mecanografiado en mi nuevo trabajo: D

Excelente. Quiero usar este tipo en TS1.8.

También me gustaría ver algo como un tipo object . Sin embargo, me pregunto si debería ajustarse a typeof value === 'object' , ya que esto excluiría funciones. En cambio, me parece que el tipo object prospectivo debería reflejar el tipo de lenguaje Object, que incluye funciones. Hay algunas razones para esto:

  • Las API que aceptan objetos planos siempre, hasta donde yo sé, también aceptan funciones.
  • Las funciones heredan del constructor Object .

Los tomaré en orden.

API

Las API como WeakMap que solo aceptan objetos aceptan el tipo de lenguaje Object, no solo objetos simples. Lo siguiente está bien:

function theAnswer() {}

let map = new WeakMap();

map.set(theAnswer, 42);
console.log(map.get(theAnswer)); // 42

Esto es tan cierto para las API personalizadas como para las integradas. Si espero un objeto, por lo general solo quiero un paquete de propiedades. Las funciones son simplemente conjuntos de propiedades invocables.

Herencia

Dado que Function extiende el constructor Object , también podemos usar los métodos estáticos y de instancia disponibles en Object en funciones. Por ejemplo, es posible lo siguiente:

class DeepThought {
  static getAnswer() {
    return 42;
  }
}

let computer = Object.create(DeepThought);

console.log(computer.getAnswer()); // 42
console.log(Object.getPrototypeOf(computer)); // [Function: DeepThought]

Si bien el ejemplo anterior es un poco tonto, solo ilustra que las API que usan los métodos Object internamente también podrían aceptar funciones.

Por lo tanto, sugeriría que el tipo object ajuste a lo siguiente, que corresponde al tipo de lenguaje Object (excepto que incluye null , pero no hay protección contra null en TypeScript En todo caso).

function isObject(value) {
  return typeof value === 'object' || typeof value === 'function';
}

Caso de uso

Tengo un proyecto llamado Deep Map que recurre a los pares clave-valor de objetos anidados y transforma valores primitivos en el camino. Como tal, distingue entre tipos primitivos y no primitivos. Tuve que definir una interfaz NonPrimitive personalizada, de la siguiente manera:

interface NonPrimitive {
  [key: string]: any;
  [index: number]: any;
}

export class DeepMap {
  // ...
  private mapObject(obj: NonPrimitive): NonPrimitive { /* ... */ }
}

Esta no es la primera vez que tengo que definir la interfaz NonPrimitive . Sería bueno si pudiera hacer esto:

export class DeepMap {
  // ...
  private mapObject(obj: object): object { /* ... */ }
}

Como sugerí anteriormente, realmente no me importa si el parámetro obj es invocable o no. Todo lo que importa es que es del Object _tipo de idioma_, es decir, que es un conjunto de propiedades cuyos pares clave-valor puedo recorrer en iteración.

Estoy de acuerdo en que las funciones son object s. La intención es excluir primitivas.

Ok, pensé que esa debía haber sido la intención. Pensé que tal vez me estaba perdiendo algo con toda esta charla de typeof value === 'object' .

¿Permitiría este tipo object asignación de propiedades ad-hoc (como any )? Por ejemplo:

const foo: object = {};

foo.bar = 'baz';

No

¿Cuál es la diferencia práctica entre

export class DeepMap {
  // ...
  private mapObject(obj: object): object { /* ... */ }
}

y

export class DeepMap {
  // ...
  private mapObject(obj: Object): Object { /* ... */ }
}

? (Usando object vs Object ). Me parece que un Function extiende desde Object , así que no veo por qué no podemos hacer eso ya. ¿Puedes explicar?

@trusktr Todos los valores distintos de null y undefined pueden asignar al tipo Object ; una función que acepta Object tomará una cadena, un número, un booleano o un símbolo. Solo los valores no primitivos se pueden asignar a object ; pasar una cadena, etc. producirá un error en tiempo de compilación. El tipo object es útil cuando esperamos un valor no primitivo, pero no nos importa cuáles son sus propiedades.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

dlaberge picture dlaberge  ·  3Comentarios

uber5001 picture uber5001  ·  3Comentarios

DanielRosenwasser picture DanielRosenwasser  ·  3Comentarios

kyasbal-1994 picture kyasbal-1994  ·  3Comentarios

fwanicka picture fwanicka  ·  3Comentarios