Typescript: Suggestion: Le type primitif `objet`

Créé le 26 janv. 2015  ·  17Commentaires  ·  Source: microsoft/TypeScript

Le type primitif object

Cette proposition décrit un nouveau type primitif pour dactylographié object .

Cas d'utilisation

L'API de base de JavaScript contient des fonctions qui prennent object comme paramètre:

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

Actuellement, il n'y a aucun moyen dans le typographie d'empêcher de passer d'autres types primitifs ( string , number , etc ...) à ces fonctions.
Par exemple Object.create('string') est une instruction dactylographiée parfaitement valide, même si elle aboutit à une erreur.
Créer un nouveau type primitif object permettrait de modéliser plus correctement la signature de ces fonctions, et de chaque fonction qui partage une contrainte similaire.

Vérification de type

Assignabilité

Le type object est l'équivalent de {} moins l'assignabilité d'un autre type de base, ce qui signifie que:

  • tous les autres types de base ne sont pas assignables à object
  • tout type non basique est assignable à object
  • object n'est assignable qu'à {} et any

Ce comportement est cohérent avec le fonctionnement de javascript, le type représente chaque valeur qui respecte la contrainte typeof value === 'object' plus undefined .

Type de garde

Une nouvelle protection de type doit être introduite pour object : typeof value === 'object' .
En option, le compilateur peut également accepter la comparaison avec la fonction Object cast: Object(value) === value .

Suggestion help wanted

Commentaire le plus utile

+1 sur cette proposition. Je ne considérerais pas WeakMap une API "experte", mais je considère que c'est une raison suffisante à elle seule pour l'implémenter. Puisque le runtime fait une distinction entre les types d'objets et les types primitifs, il est logique d'avoir une fonction dans TypeScript pour faire la distinction également.

Cette proposition résoudra également le problème de la saisie de "sacs d'objets" où chaque propriété est facultative, mais les autres types primitifs devraient être interdits.

Tous les 17 commentaires

Il serait utile de lister certaines API autres que les fonctions Object. qui en bénéficieraient.

Dans l'API du noyau javascript, sauf à partir de Object peut-être que certaines constructions es6 en bénéficieront (WeakMap, Proxy, etc.).
Mais par exemple, chaque fonction de collecte de soulignement gagnerait également un meilleur typage, un framework comme React utilise une méthode 'setState' qui n'accepte que des objets ou null , ImmutableJS, Mori, etc.

Edit: la collection de soulignements fonctionne avec tous les types

Discussion lors de la réunion d'examen des suggestions. Tout cela a du sens, mais nous devons justifier la complexité d'un nouveau type primitif par le nombre d'erreurs / la richesse de la description qu'il est capable de fournir. En gros, plus d'exemples d'API qui ne peuvent pas fonctionner sur des primitives, en particulier celles qui ne sont pas des API "expertes" (par exemple, les proxies)

Poster plus comme référence que comme recommandation: clin d'oeil:

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 sur cette proposition. Je ne considérerais pas WeakMap une API "experte", mais je considère que c'est une raison suffisante à elle seule pour l'implémenter. Puisque le runtime fait une distinction entre les types d'objets et les types primitifs, il est logique d'avoir une fonction dans TypeScript pour faire la distinction également.

Cette proposition résoudra également le problème de la saisie de "sacs d'objets" où chaque propriété est facultative, mais les autres types primitifs devraient être interdits.

Object.observe nécessite également une cible non primitive: http://arv.github.io/ecmascript-object-observe/#Object.observe

Je pense que le titre est un peu déroutant puisqu'il mentionne le mot «primitif». Cela peut également être appelé _ "Type d'objet ECMAScript" _ ou _ "Type d'objet d'exécution" _. Ce qui serait assignable à partir de tout autre que les types primitifs (y compris le type undefined et les symboles), les types de fonction ou de constructeur. Les critères pour ce qui est un type légal object doivent être basés sur la garde suivante:

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

Selon MDN , c'est la sortie de typeof :

Indéfini : "non défini"
Null : "objet" (voir ci-dessous)
Boolean : "boolean"
Numéro : "nombre"
Chaîne : "chaîne"
Symbole (nouveau dans ECMAScript 2015) : "symbole"
Objet hôte (fourni par l'environnement JS) : dépendant de l'implémentation
Objet fonction (implémente [[Appel]] en termes ECMA-262) : "fonction"
Tout autre objet : "objet"

Sans ce type, il n'y a aucun moyen de faire correspondre correctement un type précis pour la garde mentionnée ci-dessus, pas même en tant que garde définie par l'utilisateur. Ceci est également essentiel pour une utilisation avec toute fonction qui nécessite strictement un objet qui a des propriétés et autorise for in boucles et appels au prototype Object , et pour implémenter des contraintes génériques comme T extends object puis référencer en toute sécurité les propriétés ou utiliser les fonctions du prototype.

L'exclusion des fonctions et des constructeurs serait toutefois un peu limitative dans certains cas, il pourrait donc y avoir un autre type qui les inclurait également et appelé le _ "type d'objet non primitif" _. Une solution de contournement partielle pourrait être l'union object | Function bien que des transtypages ou des gardes supplémentaires soient nécessaires pour le gérer correctement.

Accepter les PR. Cela semble être un problème courant; ce sera un changement radical pour quiconque est assez courageux pour avoir écrit interface object { ... } . La mise en œuvre doit être simple et suivre les règles décrites ci-dessus.

Note latérale: nous nous demandons tous où a été @fdecampredon !

@RyanCavanaugh Changement de ville de travail, et malheureusement pas beaucoup de tapuscrit dans mon nouvel emploi: D

Génial. Je souhaite utiliser ce type dans TS1.8.

J'aimerais aussi voir quelque chose comme un type object . Cependant, je me demande s'il doit être conforme à typeof value === 'object' , car cela exclurait les fonctions. Au lieu de cela, il me semble que le type potentiel object devrait refléter le type de langage Object, qui inclut des fonctions. Il y a plusieurs raisons à cela:

  • Les API qui acceptent des objets simples toujours, autant que je sache, acceptent également des fonctions.
  • Les fonctions héritent du constructeur Object .

Je vais les prendre dans l'ordre.

Apis

Les API telles que WeakMap qui n'acceptent que des objets acceptent le type de langage Object, pas seulement des objets simples. Ce qui suit est ok:

function theAnswer() {}

let map = new WeakMap();

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

Cela est aussi vrai pour les API personnalisées que pour les API intégrées. Si j'attends un objet, je veux généralement juste un ensemble de propriétés. Les fonctions ne sont que des ensembles de propriétés appelables.

Héritage

Puisque Function étend le constructeur Object , nous pouvons également utiliser les méthodes statiques et d'instance disponibles sur Object sur les fonctions. Par exemple, ce qui suit est possible:

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

let computer = Object.create(DeepThought);

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

Bien que l'exemple ci-dessus soit un peu idiot, il illustre simplement que les API qui utilisent les méthodes Object interne pourraient tout aussi bien accepter des fonctions.

Je suggérerais donc que le type object conforme à ce qui suit, qui correspond au type de langage Object (sauf qu'il inclut null , mais il n'y a pas de protection contre null dans TypeScript dans tout les cas).

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

Cas d'utilisation

J'ai un projet appelé Deep Map qui se répète à travers les paires clé-valeur d'objets imbriqués et transforme les valeurs primitives en cours de route. En tant que tel, il fait la distinction entre les types primitifs et non primitifs. J'ai dû définir une interface personnalisée NonPrimitive , comme suit:

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

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

Ce n'est pas la première fois que je dois définir l'interface NonPrimitive . Ce serait bien si je pouvais simplement faire ceci:

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

Comme je l'ai suggéré ci-dessus, je ne me soucie pas vraiment de savoir si le paramètre obj est appelable ou non. Tout ce qui compte, c'est qu'il soit du type Object _language_, c'est-à-dire qu'il s'agit d'un ensemble de propriétés dont je peux parcourir les paires clé-valeur.

Je conviens que les fonctions sont object s. L'intention est d'exclure les primitives.

Ok, je pensais que ça devait être l'intention. Je pensais juste que je manquais peut-être quelque chose avec tout ce discours sur typeof value === 'object' .

ce type object autoriserait-il l'attribution de propriété ad hoc (comme any )? Par exemple:

const foo: object = {};

foo.bar = 'baz';

Non

Quelle est la différence pratique entre

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

et

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

? (En utilisant object vs Object ). Il me semble qu'un Function s'étend de Object , donc je ne vois pas pourquoi nous ne pouvons pas déjà faire ça. Peux-tu expliquer?

@trusktr Toutes les valeurs autres que null et undefined sont assignables au type Object ; une fonction qui accepte un Object prendra une chaîne, un nombre, un booléen ou un symbole. Seules les valeurs non primitives peuvent être affectées à object ; passer une chaîne, etc., produira une erreur de compilation. Le type object est utile lorsque nous attendons une valeur non primitive, mais où nous ne nous soucions pas de ses propriétés.

Cette page vous a été utile?
0 / 5 - 0 notes