Expressions

Expressions and types

A type claim is erased, a shape the GPU has a word for is kept, and a value TypeScript would build at run time is refused where it is written.

  • Destructuring

    One declaration per name, in the order written.

    TypeScript
    export function swapped(v: vec2): vec2 {
    const { x, y } = v
    return vec2(y, x)
    }
    Emitted WGSL
    fn swapped(v: vec2<f32>) -> vec2<f32> {
    let x = v.x;
    let y = v.y;
    return vec2<f32>(y, x);
    }
  • Object spread

    One read per field of the struct, with the fields written after it over them.

    TypeScript
    export function lifted(p: Point): Point {
    return { ...p, y: 9. }
    }
    Emitted WGSL
    fn lifted(p: Point) -> Point {
    return Point(p.x, 9.0);
    }
  • Array literal

    An array initializer, and only where the declaration states array<T, N>. array-literal-ramp

    TypeScript
    export function step3(i: i32): f32 {
    const xs: array<f32, 3> = [0.15, 0.5, 0.9]
    return xs[i]
    }
    Emitted WGSL
    fn step3(i: i32) -> f32 {
    let xs = array<f32, 3>(0.15, 0.5, 0.9);
    return xs[i];
    }
  • Tuple

    A tuple is an array of a length the type fixes, a return included. tuple-and-brand

    TypeScript
    export function bounds(x: f32): [f32, f32] {
    return [x - 1., x + 1.]
    }
    Emitted WGSL
    fn bounds(x: f32) -> array<f32, 2> {
    return array<f32, 2>((x - 1.0), (x + 1.0));
    }
  • Literal union

    A union whose members all name one type names that type.

    TypeScript
    export function isWire(m: 0 | 1 | 2): bool {
    return m === 2
    }
    Emitted WGSL
    fn isWire(m: i32) -> bool {
    return (m == 2);
    }
  • Branded type

    The brand is erased and the parameter is an f32. tuple-and-brand

    TypeScript
    type Metres = f32 & { readonly [m]: 'metres' }
    Emitted WGSL
    fn deeper(d: f32) -> f32 {
    return (d * 2.0);
    }
  • Type claims

    A claim about a type, not a conversion, so each emits what its operand emits.

    TypeScript
    export function claimed(x: f32): f32 {
    const a = x as f32
    const b = (x satisfies f32)!
    const k = 2. as const
    return a + b * k
    }
    Emitted WGSL
    fn claimed(x: f32) -> f32 {
    return (x + (x * 2.0));
    }
  • Exponent operator

    pow on both targets.

    TypeScript
    export function curved(x: f32): f32 {
    return x ** 2.2
    }
    Emitted WGSL
    fn curved(x: f32) -> f32 {
    return pow(x, 2.2);
    }
  • && on a bool

    The operator both targets have, on one bool at a time.

    TypeScript
    export function both(a: bool, b: bool): bool {
    return a && b
    }
    Emitted WGSL
    fn both(a: bool, b: bool) -> bool {
    return (a && b);
    }
  • && on a mask

    Refused

    Combine masks with all, any or a select. bool-select

    TypeScript
    export function both(a: vec2b, b: vec2b): bool {
    return a && b
    }

    Compiler diagnostic

    TS8003 Logical "&&" requires bool operands.

  • Optional field

    Refused

    A struct field is always there in the memory the host fills.

    TypeScript
    class Disc {
    center: vec2
    radius?: f32
    }

    Compiler diagnostic

    TS8010 Optional field "radius?" on "Disc" is not supported: a struct field is always present in the buffer the host fills.

  • String value

    Refused

    There is no string on the GPU; write the cases as an enum.

    TypeScript
    export function label(x: f32): f32 {
    const name = `band ${x}`
    return x
    }

    Compiler diagnostic

    TS8013 Template strings are JS. TypeShade has no string type.

  • number

    Refused

    A number on the GPU has a width.

    TypeScript
    export function twice(x: number): number {
    return x * 2
    }

    Compiler diagnostic

    TS8002 A number on the GPU has a width. Write f32 for a float, i32 or u32 for an integer.

  • boolean

    Refused

    The shader spelling is bool.

    TypeScript
    export function on(x: boolean): f32 {
    return x ? 1. : 0.
    }

    Compiler diagnostic

    TS8002 TypeShade spells the boolean "bool".

  • Integer literal

    An integer-written local initializer can take the i32 or u32 required by a declared nongeneric function, a constructor or a method argument. Explicitly typed initialization and simple assignment can establish the same demand in either direction when the type is known independently of the local. Conflicting concrete types produce TS8003; use an annotation or cast to settle the type. With no integer demand, an unannotated local remains f32. Float-written initializers, module constants and loop induction variables keep their existing rules.

    TypeScript
    class Hit {
    constructor(public index: i32) {}
    offset(value: i32): i32 { return this.index + value; }
    }
    export function counts(): i32 {
    let n = -1;
    const hit = new Hit(n);
    n = hit.index;
    return hit.offset(n);
    }
    Emitted WGSL
    fn Hit_new(index: i32) -> Hit {
    var self_: Hit = Hit(0);
    self_.index = index;
    return self_;
    }
    fn Hit_offset(self_: Hit, value: i32) -> i32 {
    return (self_.index + value);
    }
    fn counts() -> i32 {
    var n: i32 = -1;
    let hit = Hit_new(n);
    n = hit.index;
    return Hit_offset(hit, n);
    }
  • Increment and decrement

    An assignment of the value one step on.

    TypeScript
    export function stepped(x: f32): f32 {
    let acc: f32 = x
    acc++
    acc--
    return acc
    }
    Emitted WGSL
    fn stepped(x: f32) -> f32 {
    var acc: f32 = x;
    acc = (acc + 1.0);
    acc = (acc - 1.0);
    return acc;
    }

Edit this page Report a problem