resources > Release Notes & Upgrade Guides > AssemblyScript Migrationsguide

AssemblyScript Migrationsguide

Reading time: 10 min

Hittills har undergrafar använt en av de första versionerna av AssemblyScript (v0.6). Äntligen har vi lagt till stöd för den nyaste tillgängliga versionen (v0.19.10)! 🎉

Det kommer att möjliggöra för undergrafutvecklare att använda nyare funktioner i AS-språket och standardbiblioteket.

Denna guide är tillämplig för alla som använder graph-cli/graph-ts version 0.22.0 eller lägre. Om du redan är på en högre version än (eller lika med) det, har du redan använt version 0.19.10 av AssemblyScript 🙂

Observera: Från och med 0.24.0 kan graph-node stödja båda versionerna, beroende på apiVersion som anges i undergrafens manifest.

Funktioner

Länk till detta avsnitt

Ny funktionalitet

Länk till detta avsnitt
  • TypedArrays kan nu skapas från ArrayBuffers med hjälp av det nya wrap-statiska metoden (v0.8.1)
  • Nya standardbiblioteksfunktioner: String#toUpperCase, String#toLowerCase, String#localeCompare och TypedArray#set (v0.9.0)
  • Lagt till stöd för x instanceof GenericClass (v0.9.2)
  • Lagt till StaticArray<T>, en mer effektiv varian av en array (v0.9.3)
  • Lagt till Array<T>#flat (v0.10.0)
  • Implementerat radix-argumentet på Number#toString (v0.10.1)
  • Lagt till stöd för avskiljare i flyttal (v0.13.7)
  • Lagt till stöd för funktioner av första klass (v0.14.0)
  • Lägg till inbyggda funktioner: i32/i64/f32/f64.add/sub/mul (v0.14.13)
  • Implementera Array/TypedArray/String#at (v0.18.2)
  • Lagt till stöd för mallliteralsträngar (v0.18.17)
  • Lägg till encodeURI(Component) och decodeURI(Component) (v0.18.27)
  • Lägg till toString, toDateString och toTimeString för Date (v0.18.29)
  • Lägg till toUTCString för Date (v0.18.30)
  • Lägg till inbyggd typ nonnull/NonNullable (v0.19.2)
  • Math-funktioner som exp, exp2, log, log2 och pow har ersatts med snabbare varianter (v0.9.0)
  • Lätt optimering av Math.mod (v0.17.1)
  • Cachea fler fältåtkomster i std Map och Set (v0.17.8)
  • Optimering för potenser av två i ipow32/64 (v0.18.2)
  • Typen för en arrayliteral kan nu härledas från dess innehåll (v0.9.0)
  • Uppdaterad stdlib till Unicode 13.0.0 (v0.10.0)

Hur uppgraderar du?

Länk till detta avsnitt
  1. Ändra dina mappningar apiVersion i subgraph.yaml till 0.0.6:
...
dataSources:
...
mapping:
...
apiVersion: 0.0.6
...
  1. Uppdatera graph-cli som du använder till den nyaste versionen genom att köra:
# om du har den globalt installerad
npm install --global @graphprotocol/graph-cli@latest
# eller i din subgraf om du har det som ett utvecklingsberoende
npm install --save-dev @graphprotocol/graph-cli@latest
  1. Gör samma sak för graph-ts, men istället för att installera globalt, spara den i dina huvudberoenden:
npm install --save @graphprotocol/graph-ts@latest
  1. Följ resten av guiden för att åtgärda språkbrytande ändringar.
  2. Kör codegen och deploy igen.

Språkbrytande ändringar

Länk till detta avsnitt

I den äldre versionen av AssemblyScript kunde du skapa kod som detta:

function load(): Value | null { ... }
let maybeValue = load();
maybeValue.aMethod();

Men i den nyare versionen, eftersom värdet är nullable, måste du kontrollera, så här:

let maybeValue = load()
if (maybeValue) {
maybeValue.aMethod() // `maybeValue` is not null anymore
}

Eller gör så här:

let maybeValue = load()! // bryts i runtime om värdet är null
maybeValue.aMethod()

Om du är osäker på vilken du ska välja, rekommenderar vi alltid att använda den säkra versionen. Om värdet inte finns kanske du bara vill göra ett tidigt villkorligt uttalande med en retur i din undergrafshanterare.

Variabelskuggning

Länk till detta avsnitt

Tidigare kunde du använda variabelskuggning och kod som detta skulle fungera:

let a = 10
let b = 20
let a = a + b

Men nu är detta inte längre möjligt, och kompilatorn returnerar detta fel:

ERROR TS2451: Cannot redeclare block-scoped variable 'a'
let a = a + b;
~~~~~~~~~~~~~
in assembly/index.ts(4,3)

Du måste döpa om dina duplicerade variabler om du hade variabelskuggning.

Jämförelser med nollvärden

Länk till detta avsnitt

När du gör uppgraderingen av din subgraf kan du ibland få fel som dessa:

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)

För att lösa problemet kan du helt enkelt ändra if-satsen till något i den här stilen:

if (!decimals) {
// or
if (decimals === null) {

Samma gäller om du använder != istället för ==.

Det vanliga sättet att göra kasting tidigare var att bara använda nyckelordet as, som så här:

let byteArray = new ByteArray(10)
let uint8Array = byteArray as Uint8Array // motsvarande: <Uint8Array>byteArray

Detta fungerar dock endast i två scenarier:

  • Primitiv kasting (mellan typer som u8, i32, bool; t.ex. let b: isize = 10; b as usize);
  • Uppkasting vid klassarv (underklass → överklass)

Exempel:

// primitive casting
let a: usize = 10
let b: isize = 5
let c: usize = a + (b as usize)
// upcasting on class inheritance
class Bytes extends Uint8Array {}
let bytes = new Bytes(2)
// <Uint8Array>bytes // same as: bytes as Uint8Array

Det finns två scenarier där du kan vilja casta, men att använda as/<T>var är inte säkert:

  • Downcasting vid arv av klasser (superklass → subklass)
  • Mellan två typer som delar en superklass
// downcasting om klassarv
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
// <Bytes>uint8Array // breaks in runtime :(
// mellan två typer som delar en superklass
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
// <ByteArray>bytes // breaks in runtime :(

I dessa fall kan du använda funktionen changetype<T>:

// downcasting om klassarv
class Bytes extends Uint8Array {}
let uint8Array = new Uint8Array(2)
changetype<Bytes>(uint8Array) // works :)
// mellan två typer som delar en superklass
class Bytes extends Uint8Array {}
class ByteArray extends Uint8Array {}
let bytes = new Bytes(2)
changetype<ByteArray>(bytes) // works :)

Om du bara vill ta bort nullability kan du fortsätta använda as-operatorn (eller <T>variable), men se till att du vet att värdet inte kan vara null, annars kommer det att bryta.

// ta bort ogiltighet
let previousBalance = AccountBalance.load(balanceId) // AccountBalance | null
if (previousBalance != null) {
return previousBalance as AccountBalance // safe remove null
}
let newBalance = new AccountBalance(balanceId)

För nullbarhetsfallet rekommenderar vi att du tittar på nullbarhetskontrollfunktionen, den kommer att göra din kod renare 🙂

Vi har också lagt till några fler statiska metoder i vissa typer för att underlätta kastning, de är:

  • Bytes.fromByteArray
  • Bytes.fromUint8Array
  • BigInt.fromByteArray
  • ByteArray.fromBigInt

Kontroll av nollställbarhet med tillgång till egendom

Länk till detta avsnitt

För att använda nullability check feature kan du använda antingen if-satser eller den ternära operatorn (? och :) så här:

let something: string | null = 'data'
let somethingOrElse = something ? something : 'else'
// or
let somethingOrElse
if (something) {
somethingOrElse = something
} else {
somethingOrElse = 'else'
}

Men det fungerar bara när du gör if / ternary på en variabel, inte på en egenskapstillgång, som den här:

class Container {
data: string | null
}
let container = new Container()
container.data = 'data'
let somethingOrElse: string = container.data ? container.data : 'else' // Kompilerar inte

Vilket ger detta fel:

ERROR TS2322: Type '~lib/string/String | null' is not assignable to type '~lib/string/String'.
let somethingOrElse: string = container.data ? container.data : "else";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

För att åtgärda problemet kan du skapa en variabel för den egenskapen så att kompilatorn kan utföra den magiska nollbarhetskontrollen:

class Container {
data: string | null
}
let container = new Container()
container.data = 'data'
let data = container.data
let somethingOrElse: string = data ? data : 'else' // kompilerar helt okej :)

Operatörsöverladdning med egenskapsaccess

Länk till detta avsnitt

Om du försöker summera (till exempel) en nullable typ (från en property access) med en non nullable, kommer AssemblyScript-kompilatorn istället för att ge en kompileringsfelsvarning om att ett av värdena är nullable, bara att kompilera tyst, vilket gör att koden kan gå sönder vid körning.

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 = null
x + y // ge kompileringsfel om ogiltighet
let wrapper = new Wrapper(y)
wrapper.n = wrapper.n + x // ger inte kompileringsfel som det borde

Vi har öppnat en fråga om AssemblyScript-kompilatorn för detta, men om du gör den här typen av operationer i dina subgraf-mappningar bör du ändra dem så att de gör en null-kontroll innan den.

let wrapper = new Wrapper(y)
if (!wrapper.n) {
wrapper.n = BigInt.fromI32(0)
}
wrapper.n = wrapper.n + x // nu är `n` garanterat ett BigInt

Initialisering av värde

Länk till detta avsnitt

Om du har någon kod som denna:

var value: Type // null
value.x = 10
value.y = 'content'

Det kommer att kompilera men brytas vid körning, det händer eftersom värdet inte har initialiserats, så se till att din subgraf har initialiserat sina värden, så här:

var value = new Type() // initialized
value.x = 10
value.y = 'content'

Även om du har nullable properties i en GraphQL-entitet, som denna:

type Total @entity {
id: Bytes!
amount: BigInt
}

Och du har en kod som liknar den här:

let total = Total.load('latest')
if (total === null) {
total = new Total('latest')
}
total.amount = total.amount + BigInt.fromI32(1)

Du måste se till att initialisera värdet total.amount, för om du försöker komma åt som i den sista raden för summan, kommer det att krascha. Så antingen initialiserar du det först:

let total = Total.load('latest')
if (total === null) {
total = new Total('latest')
total.amount = BigInt.fromI32(0)
}
total.tokens = total.tokens + BigInt.fromI32(1)

Eller så kan du bara ändra ditt GraphQL-schema för att inte använda en nullable-typ för den här egenskapen, då initierar vi den som noll i codegen -steget 😉

type Total @entity {
id: Bytes!
amount: BigInt!
}
let total = Total.load('latest')
if (total === null) {
total = new Total('latest') // initierar redan icke-nullställbara egenskaper
}
total.amount = total.amount + BigInt.fromI32(1)

Initialisering av klassegenskaper

Länk till detta avsnitt

Om du exporterar några klasser med egenskaper som är andra klasser (deklarerade av dig eller av standardbiblioteket) på det här sättet:

class Thing {}
export class Something {
value: Thing
}

Kompilatorn kommer att göra fel eftersom du antingen måste lägga till en initialiserare för de egenskaper som är klasser, eller lägga till operatorn !:

export class Something {
constructor(public value: Thing) {}
}
// or
export class Something {
value: Thing
constructor(value: Thing) {
this.value = value
}
}
// or
export class Something {
value!: Thing
}

Initialisering av Array

Länk till detta avsnitt

Klassen Array accepterar fortfarande ett tal för att initiera längden på listan, men du bör vara försiktig eftersom operationer som .push faktiskt ökar storleken istället för att lägga till i början, till exempel:

let arr = new Array<string>(5) // ["", "", "", "", ""]
arr.push('something') // ["", "", "", "", "", "something"] // size 6 :(

Beroende på vilka typer du använder, t.ex. nullable-typer, och hur du kommer åt dem, kan du stöta på ett runtime-fel som det här:

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

För att faktiskt trycka i början bör du antingen initiera Array med storlek noll, så här:

let arr = new Array<string>(0) // []
arr.push('something') // ["something"]

Eller så bör du mutera den via index:

let arr = new Array<string>(5) // ["", "", "", "", ""]
arr[0] = 'something' // ["something", "", "", "", ""]

GraphQL-schema

Länk till detta avsnitt

Detta är inte en direkt AssemblyScript-ändring, men du kan behöva uppdatera din schema.graphql-fil.

Nu kan du inte längre definiera fält i dina typer som är Non-Nullable Lists. Om du har ett schema som detta:

type Something @entity {
id: Bytes!
}
type MyEntity @entity {
id: Bytes!
invalidField: [Something]! # no longer valid
}

Du måste lägga till en ! till medlemmen i List-typen, så här:

type Something @entity {
id: Bytes!
}
type MyEntity @entity {
id: Bytes!
invalidField: [Something!]! # valid
}

Detta ändrades på grund av skillnader i nullbarhet mellan AssemblyScript-versioner, och det är relaterat till filen src/generated/schema.ts (standardväg, du kanske har ändrat detta).

  • Jämnade Map#set och Set#add med specifikationen, som returnerar this (v0.9.2)
  • Arrayer ärver inte längre från ArrayBufferView, men är nu distinkta (v0.10.0)
  • Klasser som initialiseras från objektlitteraler kan inte längre definiera en konstruktor (v0.10.0)
  • Resultatet av en binär **-operation är nu det gemensamma nämnaren för heltal om båda operanderna är heltal. Tidigare var resultatet ett flyttal som om man anropade Math/f.pow (v0.11.0)
  • Tvinga NaN till false vid kastning till bool (v0.14.9)
  • När du skiftar en liten heltalsvärde av typ i8/u8 eller i16/u16, påverkar endast de 3 respektive 4 minst signifikanta bitarna i RHS-värdet resultatet, analogt med resultatet av en i32.shl som endast påverkas av de 5 minst signifikanta bitarna i RHS-värdet. Exempel: someI8 << 8 producerade tidigare värdet 0, men producerar nu someI8 på grund av maskeringen av RHS som 8 & 7 = 0 (3 bitar) (v0.17.0)
  • Buggfix för relationella strängjämförelser när storlekarna skiljer sig (v0.17.8)
Redigera sida

Tidigare
Kuratering
Nästa
Migrationsguide för GraphQL-validering
Redigera sida