AssemblyScript Migrationsguide
Reading time: 10 min
Hittills har undergrafar använt en av de (v0.6). Äntligen har vi lagt till stöd för den (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.
TypedArray
s kan nu skapas frånArrayBuffer
s med hjälp av ()- Nya standardbiblioteksfunktioner:
String#toUpperCase
,String#toLowerCase
,String#localeCompare
ochTypedArray#set
() - Lagt till stöd för x instanceof GenericClass ()
- Lagt till
StaticArray<T>
, en mer effektiv varian av en array () - Lagt till
Array<T>#flat
() - Implementerat
radix
-argumentet påNumber#toString
() - Lagt till stöd för avskiljare i flyttal ()
- Lagt till stöd för funktioner av första klass ()
- Lägg till inbyggda funktioner:
i32/i64/f32/f64.add/sub/mul
() - Implementera
Array/TypedArray/String#at
() - Lagt till stöd för mallliteralsträngar ()
- Lägg till
encodeURI(Component)
ochdecodeURI(Component)
() - Lägg till
toString
,toDateString
ochtoTimeString
förDate
() - Lägg till
toUTCString
förDate
() - Lägg till inbyggd typ
nonnull/NonNullable
()
Math
-funktioner somexp
,exp2
,log
,log2
ochpow
har ersatts med snabbare varianter ()- Lätt optimering av
Math.mod
() - Cachea fler fältåtkomster i std Map och Set ()
- Optimering för potenser av två i
ipow32/64
()
- Typen för en arrayliteral kan nu härledas från dess innehåll ()
- Uppdaterad stdlib till Unicode 13.0.0 ()
- Ändra dina mappningar
apiVersion
isubgraph.yaml
till0.0.6
:
...dataSources:...mapping:...apiVersion: 0.0.6...
- Uppdatera
graph-cli
som du använder till dennyaste
versionen genom att köra:
# om du har den globalt installeradnpm install --global @graphprotocol/graph-cli@latest# eller i din subgraf om du har det som ett utvecklingsberoendenpm install --save-dev @graphprotocol/graph-cli@latest
- 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
- Följ resten av guiden för att åtgärda språkbrytande ändringar.
- Kör
codegen
ochdeploy
igen.
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 nullmaybeValue.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.
Tidigare kunde du använda och kod som detta skulle fungera:
let a = 10let b = 20let 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.
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) {// orif (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 castinglet a: usize = 10let b: isize = 5let c: usize = a + (b as usize)
// upcasting on class inheritanceclass 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 klassarvclass Bytes extends Uint8Array {}let uint8Array = new Uint8Array(2)// <Bytes>uint8Array // breaks in runtime :(
// mellan två typer som delar en superklassclass 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 klassarvclass Bytes extends Uint8Array {}let uint8Array = new Uint8Array(2)changetype<Bytes>(uint8Array) // works :)
// mellan två typer som delar en superklassclass 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 ogiltighetlet previousBalance = AccountBalance.load(balanceId) // AccountBalance | nullif (previousBalance != null) {return previousBalance as AccountBalance // safe remove null}let newBalance = new AccountBalance(balanceId)
För nullbarhetsfallet rekommenderar vi att du tittar på , 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
För att använda 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'// orlet somethingOrElseif (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.datalet somethingOrElse: string = data ? data : 'else' // kompilerar helt okej :)
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 = nullx + y // ge kompileringsfel om ogiltighetlet 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
Om du har någon kod som denna:
var value: Type // nullvalue.x = 10value.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() // initializedvalue.x = 10value.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)
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) {}}// orexport class Something {value: Thingconstructor(value: Thing) {this.value = value}}// orexport class Something {value!: Thing}
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", "", "", "", ""]
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
ochSet#add
med specifikationen, som returnerarthis
() - Arrayer ärver inte längre från ArrayBufferView, men är nu distinkta ()
- Klasser som initialiseras från objektlitteraler kan inte längre definiera en konstruktor ()
- 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 anropadeMath/f.pow
() - Tvinga
NaN
tillfalse
vid kastning tillbool
() - När du skiftar en liten heltalsvärde av typ
i8
/u8
elleri16
/u16
, påverkar endast de 3 respektive 4 minst signifikanta bitarna i RHS-värdet resultatet, analogt med resultatet av eni32.shl
som endast påverkas av de 5 minst signifikanta bitarna i RHS-värdet. Exempel:someI8 << 8
producerade tidigare värdet0
, men producerar nusomeI8
på grund av maskeringen av RHS som8 & 7 = 0
(3 bitar) () - Buggfix för relationella strängjämförelser när storlekarna skiljer sig ()