Guide de migration de l'AssemblyScript
Reading time: 11 min
Jusqu'à présent, les subgraphs utilisaient l'une des (v0.6). Enfin, nous avons ajouté la prise en charge du (v0.19.10) ! 🎉
Cela permettra aux développeurs de subgraph d'utiliser les nouvelles fonctionnalités du langage AS et de la bibliothèque standard.
Ce guide s'applique à toute personne utilisant graph-cli
/graph-ts
sous la version 0.22.0
. Si vous utilisez déjà une version supérieure (ou égale), vous utilisez déjà la version 0.19.10
d'AssemblyScript 🙂
Remarque : Depuis 0.24.0
, graph-node
peut prendre en charge les deux versions, en fonction de la apiVersion
spécifiée dans le manifeste du subgraph.
- Les
TypedArray
peuvent désormais être construits à partir deArrayBuffer
en utilisant les () - Nouvelles fonctions de bibliothèque standard:
String#toUpperCase
,String#toLowerCase
,String#localeCompare
andTypedArray#set
() - Ajout de la prise en charge de x instanceof GenericClass ()
- Ajout de
StaticArray<T>
, une variante de tableau plus efficace () - Ajout de
Array<T>#flat
() - Implémentation de
radix
argument onNumber#toString
() - Ajout de la prise en charge des séparateurs dans les littéraux à virgule flottante ()
- Ajout du support pour les fonctions de première classe ()
- Ajout des fonctions intégrées :
i32/i64/f32/f64.add/sub/mul
() - Implementation de
Array/TypedArray/String#at
() - Ajout de la prise en charge des chaînes littérales de modèle ()
- Ajout de
encodeURI(Component)
etdecodeURI(Component)
() - Ajout de
toString
,toDateString
ettoTimeString
àDate
() - Ajout de
toUTCString
pourDate
() - Ajout du type intégré
nonnull/NonNullable
()
- Les fonctions
Math
telles queexp
,exp2
,log
,log2
etpow
ont été remplacées par des variantes plus rapides () - Légère optimisation de
Math.mod
() - Mise en cache de plus d'accès aux champs dans std Map et Set ()
- Optimiser pour des puissances de deux
ipow32/64
()
- Le type d'un littéral de tableau peut désormais être déduit de son contenu ()
- Stdlib mis à jour vers Unicode 13.0.0 ()
- Modifiez vos mappages
apiVersion
danssubgraph.yaml
en0.0.6
:
...dataSources:...mapping:...apiVersion: 0.0.6...
- Mettez à jour le
graph-cli
que vous utilisez vers la versiondernière
en exécutant :
# si vous l'avez installé globalementnpm install --global @graphprotocol/graph-cli@latest# ou dans votre subgraph si vous l'avez comme dépendance de développementnpm install --save-dev @graphprotocol/graph-cli@latest
- Faites de même pour
graph-ts
, mais au lieu de l'installer globalement, enregistrez-le dans vos dépendances principales :
npm install --save @graphprotocol/graph-ts@latest
- Suivez le reste du guide pour corriger les changements de langue.
- Exécutez
codegen
etdeploy
à nouveau.
Sur l'ancienne version d'AssemblyScript, vous pouviez créer du code comme celui-ci :
function load(): Value | null { ... }let maybeValue = load();maybeValue.aMethod();
Cependant, sur la version la plus récente, comme la valeur est nullable, vous devez vérifier, comme ceci :
let maybeValue = load()if (maybeValue) {maybeValue.aMethod() // `maybeValue` n'est plus nul}
Ou forcez-le comme ceci :
let maybeValue = load()! // breaks in runtime if value is nullmaybeValue.aMethod()
Si vous ne savez pas lequel choisir, nous vous recommandons de toujours utiliser la version sécurisée. Si la valeur n'existe pas, vous souhaiterez peut-être simplement effectuer une instruction if précoce avec un retour dans votre gestionnaire de subgraph.
Avant de pouvoir faire de l' et un code comme celui-ci fonctionnerait :
let a = 10let b = 20let a = a + b
Cependant, cela n'est plus possible et le compilateur renvoie cette erreur :
ERROR TS2451: Cannot redeclare block-scoped variable 'a'let a = a + b;~~~~~~~~~~~~~in assembly/index.ts(4,3)
Vous devrez renommer vos variables en double si vous conservez une observation de variables.
En effectuant la mise à niveau sur votre subgraph, vous pouvez parfois obtenir des erreurs comme celles-ci :
ERROR TS2322: Type '~lib/@graphprotocol/graph-ts/common/numbers/BigInt | null' is not assignable to type '~lib/@graphprotocol/graph-ts/common/numbers/BigInt'.if (decimals == null) {~~~~in src/mappings/file.ts(41,21)
Pour résoudre, vous pouvez simplement remplacer l'instruction if
par quelque chose comme ceci :
if (!decimals) {// ou bienif (decimals === null) {
La même chose s'applique si vous faites != au lieu de ==.
Auparavant, la manière courante de faire du casting consistait simplement à utiliser le mot-clé as
, comme ceci :
let byteArray = new ByteArray(10)let uint8Array = byteArray as Uint8Array // equivalent to: <Uint8Array>byteArray
Cependant, cela ne fonctionne que dans deux scénarios :
- Casting primitif (entre des types tels que
u8
,i32
,bool
; par exemple :let b : isize = 10 ; b as usize
); - Upcasting sur l'héritage de classe (sous-classe → superclasse)
Les Exemples:
// primitive castinglet a: usize = 10let b: isize = 5let c: usize = a + (b as usize)
//upcasting lors de l'héritage de classeclass Bytes extends Uint8Array {}let bytes = new Bytes(2)// <Uint8Array>bytes // équivalent à : bytes as Uint8Array
Il existe deux scénarios dans lesquels vous souhaiterez peut-être diffuser du contenu, mais l'utilisation de as
/<T>var
n'est pas sûre :
- Downcasting sur l'héritage de classe (superclasse → sous-classe)
- Entre deux types qui partagent une superclasse
//downcasting lors de l'héritage de classeclass Bytes extends Uint8Array {}let uint8Array = new Uint8Array(2)// <Bytes>uint8Array // plante à l'exécution :(
// entre deux types qui partagent une superclasseclass Bytes extends Uint8Array {}class ByteArray extends Uint8Array {}let bytes = new Bytes(2)// <ByteArray>bytes // plante à l'exécution :(
Dans ces cas-là, vous pouvez utiliser la fonction changetype<T>
:
//downcasting lors de l'héritage de classeclass Bytes extends Uint8Array {}let uint8Array = new Uint8Array(2)changetype<Bytes>(uint8Array) // fonctionne :)
// entre deux types qui partagent une superclasseclass Bytes extends Uint8Array {}class ByteArray extends Uint8Array {}let bytes = new Bytes(2)changetype<ByteArray>(bytes) // fonctionne :)
Si vous souhaitez simplement supprimer la nullité, vous pouvez continuer à utiliser l'opérateur as
(ou <T>variable
), mais assurez-vous de savoir que la valeur ne peut pas être nulle. sinon ça va casser.
// supprimer la possibilité de valeur nulle (nullability)let previousBalance = AccountBalance.load(balanceId) // AccountBalance | nullif (previousBalance != null) {return previousBalance as AccountBalance // suppression sûre de null}let newBalance = new AccountBalance(balanceId)
Pour le cas de nullité, nous vous recommandons de jeter un œil à la , cela rendra votre code plus propre 🙂
Nous avons également ajouté quelques méthodes statiques supplémentaires dans certains types pour faciliter la diffusion, à savoir :
- Bytes.fromByteArray
- Bytes.fromUint8Array
- BigInt.fromByteArray
- ByteArray.fromBigInt
Pour utiliser la , vous pouvez utiliser soit les instructions if
, soit l'opérateur ternaire (?
et :
) comme ce:
let something: string | null = 'data'let somethingOrElse = something ? something : 'else'// oulet somethingOrElseif (something) {somethingOrElse = something} else {somethingOrElse = 'else'}
Cependant, cela ne fonctionne que lorsque vous effectuez le if
/ ternaire sur une variable, pas sur un accès à une propriété, comme ceci :
class Container {data: string | null}let container = new Container()container.data = 'data'let somethingOrElse: string = container.data ? container.data : 'else' // ne compile pas
Ce qui génère cette erreur :
ERROR TS2322: Type '~lib/string/String | null' is not assignable to type '~lib/string/String'.let somethingOrElse: string = container.data ? container.data : "else";~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pour résoudre ce problème, vous pouvez créer une variable pour l'accès à cette propriété afin que le compilateur puisse effectuer la vérification magique de la nullité :
class Container {data: string | null}let container = new Container()container.data = 'data'let data = container.datalet somethingOrElse: string = data ? data : 'else' // compile sans problème :)
Si vous essayez de additionner (par exemple) un type nullable (à partir d'un accès à une propriété) avec un type non nullable, le compilateur AssemblyScript au lieu de donner une erreur de compilation avertissant que l'une des valeurs est nullable, il compile simplement silencieusement, donnant une chance pour que le code soit interrompu au moment de l'exécution.
class BigInt extends Uint8Array {@operator('+')plus(other: BigInt): BigInt {// ...}}class Wrapper {public constructor(public n: BigInt | null) {}}let x = BigInt.fromI32(2)let y: BigInt | null = nullx + y // donne une erreur de compilation concernant la nullitélet wrapper = new Wrapper(y)wrapper.n = wrapper.n + x // ne donne pas d'erreurs de compilation comme il se doit
Nous avons ouvert un problème sur le compilateur AssemblyScript pour cela, mais pour l'instant, si vous effectuez ce type d'opérations dans vos mappages de subgraph, vous devez les modifier pour effectuer une vérification nulle avant.
let wrapper = new Wrapper(y)if (!wrapper.n) {wrapper.n = BigInt.fromI32(0)}wrapper.n = wrapper.n + x // maintenant `n` est garanti comme étant un BigInt
Si vous avez un code comme celui-ci :
var value: Type // nullvalue.x = 10value.y = 'content'
Il sera compilé mais s'arrêtera au moment de l'exécution, cela se produit parce que la valeur n'a pas été initialisée, alors assurez-vous que votre subgraph a initialisé ses valeurs, comme ceci :
var value = new Type() // initializedvalue.x = 10value.y = 'content'
De plus, si vous avez des propriétés nullables dans une entité GraphQL, comme ceci :
type Total @entity {id: Bytes!amount: BigInt}
Et vous avez un code similaire à celui-ci :
let total = Total.load('latest')if (total === null) {total = new Total('latest')}total.amount = total.amount + BigInt.fromI32(1)
Vous devrez vous assurer d'initialiser la valeur total.amount
, car si vous essayez d'accéder à la dernière ligne pour la somme, elle plantera. Donc soit vous l'initialisez d'abord :
let total = Total.load('latest')if (total === null) {total = new Total('latest')total.amount = BigInt.fromI32(0)}total.tokens = total.tokens + BigInt.fromI32(1)
Ou vous pouvez simplement modifier votre schéma GraphQL pour ne pas utiliser de type nullable pour cette propriété, puis nous l'initialiserons à zéro à l'étape codegen
😉
type Total @entity {id: Bytes!amount: BigInt!}
let total = Total.load('latest')if (total === null) {total = new Total('latest') // initialise déjà les propriétés non-nullables}total.amount = total.amount + BigInt.fromI32(1)
Si vous exportez des classes avec des propriétés qui sont d'autres classes (déclarées par vous ou par la bibliothèque standard), comme ceci :
class Thing {}export class Something {value: Thing}
Le compilateur générera une erreur car vous devez soit ajouter un initialiseur pour les propriétés qui sont des classes, soit ajouter l'opérateur !
:
export class Something {constructor(public value: Thing) {}}// ouexport class Something {value: Thingconstructor(value: Thing) {this.value = value}}// ouexport class Something {value!: Thing}
La classe Array
accepte toujours un nombre pour initialiser la longueur de la liste, mais vous devez faire attention car des opérations comme .push
augmenteront en fait la taille au lieu de l'ajouter au début. , Par exemple:
let arr = new Array<string>(5) // ["", "", "", "", ""]arr.push('something') // ["", "", "", "", "", "something"] // taille 6 :(
En fonction des types que vous utilisez, par exemple les types nullables, et de la manière dont vous y accédez, vous pourriez rencontrer une erreur d'exécution comme celle-ci :
ERRO Handler skipped due to execution failure, error: Mapping aborted at ~lib/array.ts, line 110, column 40, with message: Element type must be nullable if array is holey wasm backtrace: 0: 0x19c4 - <unknown>!~lib/@graphprotocol/graph-ts/index/format 1: 0x1e75 - <unknown>!~lib/@graphprotocol/graph-ts/common/collections/Entity#constructor 2: 0x30b9 - <unknown>!node_modules/@graphprotocol/graph-ts/global/global/id_of_type
Pour réellement pousser au début, vous devez soit initialiser le Array
avec une taille zéro, comme ceci :
let arr = new Array<string>(0) // []arr.push('quelque chose') // ["quelque chose"]
Ou vous devriez le muter via index :
let arr = new Array<string>(5) // ["", "", "", "", ""]arr[0] = 'quelque chose' // ["quelque chose", "", "", "", ""]
Il ne s'agit pas d'une modification directe d'AssemblyScript, mais vous devrez peut-être mettre à jour votre fichier schema.graphql
.
Vous ne pouvez désormais plus définir de champs dans vos types qui sont des listes non nullables. Si vous avez un schéma comme celui-ci :
type Something @entity {id: Bytes!}type MyEntity @entity {id: Bytes!invalidField: [Something]! # n'est plus valide}
Vous devrez ajouter un !
au membre de type List, comme ceci :
type Something @entity {id: Bytes!}type MyEntity @entity {id: Bytes!invalidField: [Something!]! # valide}
Cela a changé en raison des différences de nullité entre les versions d'AssemblyScript et est lié au fichier src/generated/schema.ts
(chemin par défaut, vous avez peut-être modifié cela).
- Alignement de
Map#set
etSet#add
avec la spécification, en retournantthis
() - Les tableaux n'héritent plus d'ArrayBufferView, mais sont désormais distincts ()
- Les classes initialisées à partir de littéraux d'objet ne peuvent plus définir de constructeur ()
- Le résultat d'une opération binaire
**
est désormais le dénominateur commun entier si les deux opérandes sont des entiers. Auparavant, le résultat était un float comme si vous appeliezMath/f.pow
() - Convertir
NaN
enfalse
lors de la conversion enbool
() - Lors du décalage d'une petite valeur entière de type
i8
/u8
oui16
/u16
, seuls les 3 respectivement 4 les plus petits les bits significatifs de la valeur RHS affectent le résultat, de la même manière que le résultat d'uni32.shl
n'est affecté que par les 5 bits les moins significatifs de la valeur RHS. Exemple :someI8 << 8
produisait auparavant la valeur0
, mais produit désormaissomeI8
en raison du masquage du RHS comme8 & 7 = 0
(3 bits) () - Correction d'un bug des comparaisons de chaînes relationnelles lorsque les tailles diffèrent ()
Comment mettre à niveau ?
#Lien vers cette section