TypeScript :vérifier les propriétés de l'objet et affiner le type

L'analyse du flux de contrôle de TypeScript vous permet de passer d'un type plus large à un type plus étroit :

function print(msg: any) {
if(typeof msg === 'string') {
// We know msg is a string
console.log(msg.toUpperCase()) // 👍
} else if (typeof msg === 'number') {
// I know msg is a number
console.log(msg.toFixed(2)) // 👍
}
}

Il s'agit d'un contrôle de sécurité de type en JavaScript, et TypeScript en bénéficie. Cependant, il existe des cas où TypeScript au moment d'écrire ces lignes a besoin d'un peu plus d'aide de notre part.

Supposons que vous ayez un objet JavaScript dans lequel vous ne savez pas si une certaine propriété existe. L'objet peut être any ou unknown . En JavaScript, vous vérifieriez des propriétés comme celle-ci :

if(typeof obj === 'object' && 'prop' in obj) {
//it's safe to access obj.prop
console.assert(typeof obj.prop !== 'undefined')
// But TS doesn't know :-(
}

if(typeof obj === 'object' && obj.hasOwnProperty('prop')) {
//it's safe to access obj.prop
console.assert(typeof obj.prop !== 'undefined')
// But TS doesn't know :-(
}

Pour le moment, TypeScript n'est pas en mesure d'étendre le type de obj avec un prop . Même si cela fonctionne avec JavaScript.

Nous pouvons cependant écrire une petite fonction d'assistance pour obtenir des typages corrects :

function hasOwnProperty<X extends {}, Y extends PropertyKey>
(obj: X, prop: Y): obj is X & Record<Y, unknown> {
return obj.hasOwnProperty(prop)
}

Si vous ne voulez pas savoir comment cela fonctionne, copiez-le et soyez heureux. Si vous voulez en savoir plus, voyons ce qui se passe :

  1. Notre hasOwnProperty fonction a deux génériques :
    1. X extends {} s'assure que nous n'utilisons cette méthode que sur les objets
    2. Y extends PropertyKey s'assure que la clé est soit string | number | symbol . PropertyKey est un type intégré.
  2. Il n'est pas nécessaire de définir explicitement les génériques, ils sont déduits par l'utilisation.
  3. (obj: X, prop: Y) :Nous voulons vérifier si prop est une clé de propriété de obj
  4. Le type de retour est un prédicat de type. Si la méthode renvoie true , nous pouvons retaper n'importe lequel de nos paramètres. Dans ce cas, nous disons notre obj est l'objet d'origine, avec un type d'intersection de Record<Y, unknown> , la dernière pièce ajoute la propriété nouvellement trouvée à obj et le définit sur unknown .

En cours d'utilisation, hasOwnProperty fonctionne comme ça :

// person is an object
if(typeof person === 'object'
// person = { } & Record<'name', unknown>
// = { } & { name: 'unknown'}
&& hasOwnProperty(person, 'name')
// yes! name now exists in person 👍
&& typeof person.name === 'string'
) {
// do something with person.name, which is a string
}

C'est ça! Une jolie petite aide pour que TypeScript comprenne mieux votre code.Voici un terrain de jeu pour vous bricoler.