object
Esta propuesta describe un nuevo tipo primitivo para el mecanografiado object
.
La API central de JavaScript contiene algunas funciones que toman object
como parámetro:
Object.getPrototypeOf
Object.getOwnPropertyDescriptor
Object.create
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.
El tipo object
es el equivalente a {}
menos la asignabilidad de otro tipo básico, eso significa que:
object
object
{}
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
.
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
.
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:
Object
.Los tomaré en orden.
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.
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';
}
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.
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.