Skip to content

Inspect a Go interface with a type switch ​

Type switches are for imported Go interface values. They preserve Go's dynamic type behavior while giving every case a statically narrowed binding.

Source ​

ts
import go bytes from "bytes";
import go fmt from "fmt";
import go io from "io";
import go strings from "strings";

function classify(value: io.Reader): bstring {
  switch (value) {
    case const reader as *strings.Reader {
      return "string:" + fmt.Sprint(reader.Len());
    }
    case const buffer as *bytes.Buffer {
      return "buffer:" + fmt.Sprint(buffer.Len());
    }
    case nil { return "nil"; }
    default { return "other"; }
  }
}

function main(): void {
  const text: io.Reader = strings.NewReader("hello");
  const buffer: io.Reader = bytes.NewBufferString("planet");
  const empty: io.Reader = nil;
  fmt.Println(classify(text));
  fmt.Println(classify(buffer));
  fmt.Println(classify(empty));
}

Run ​

sh
keika check type-switch.km
keika run type-switch.km

Expected output:

text
string:5
buffer:6
nil

Contract demonstrated ​

  • The switch subject evaluates once.
  • case const name as Type creates a case-local typed binding.
  • A nil interface case is distinct from default.
  • Types must be possible implementations of the subject interface.
  • Duplicate types, duplicate nil, and duplicate default are rejected.
  • Type switches never use fallthrough.

Use let instead of const when the narrowed binding itself must be reassigned, or _ when only the dynamic-type match matters.

For one assertion outside a switch, use const [reader, ok] = value as? *strings.Reader;. Use as! only when assertion failure should panic.

See Go interoperability and Language syntax.

Kinmokusei is a pre-1.0 project. Documentation describes implemented behavior.