無駄と文化

実用的ブログ

Tsify には予約された型名がある

tsify-next を使っていると特定の型名に対して想定していない型情報が生成されることがあります。
例えば Path, Range, Cow などがそうです。

// Rust

pub struct Path {
    b: bool,
    s: String,
}

#[derive(Tsify)]
pub struct Range<T> {
    _t: T,
    b: bool,
    s: String,
}

#[derive(Tsify)]
pub struct Cow<T> {
    _t: T,
    b: bool,
    s: String,
}

コンパイルすると次のような型が生成されます。

// .d.ts

export interface Path {
    b: boolean;
    s: string;
}

export interface Range<T> {
    _t: T;
    b: boolean;
    s: string;
}

export interface Cow<T> {
    _t: T;
    b: boolean;
    s: string;
}

ここまでは何もおかしなことはなく、想定している通りですね。

 

問題はこれらの型を struct のフィールドに対して指定したときにおこります。

// Rust

#[derive(Tsify)]
pub struct A {
    path: Path,
    range: Range<()>,
    cow: Cow<()>,
}

コンパイルすると想定していない結果になります。

// .d.ts

export interface A {
    path: string;
    range: { start: null; end: null };
    cow: null;
}

さきほど定義した PathRangeCow が引用されて欲しい場所がそれぞれ string, { start: null; end: null }, null になってしまっています。

 

Rust の標準の型と混同される

Rsut には Path, Range, Cow という型が標準ライブラリで定義されています。
ただしデフォルトの名前空間には読み込まれていないため、参照するにはフルパスで記述するか use 句を書いておく必要があります。

// Rust

use std::path::Path;
use std::ops::Range;
use std::borrow::Cow;

デフォルトの名前空間に読み込まれていないということは、ユーザー独自の型名に Path, Range, Cow を使用しても問題はないということです。

ただし、Tsify では問題になります。
Tsify では Path, Range, Cow などのキーワードが現れるとそれらを std::path::Path, std::ops::Range, std::borrow::Cow だと解釈して型情報を生成します。

実際、独自定義型ではなく標準型の方を使ってみると、

// Rust

#[derive(Tsify)]
pub struct B<'a> {
    path: Box<std::path::Path>,
    range: std::ops::Range<usize>,
    cow: std::borrow::Cow<'a, str>,
}
// .d.ts

export interface B {
    path: string;
    range: { start: number; end: number };
    cow: string;
}

これなら生成結果に違和感はありません。

 

Tsify には予約された型名がある

ここまでで見たように Tsify は特定の型名を標準ライブラリで定義された型だと思い込むという問題があります。 コードで言うと下記のあたりで型名がハードコードされています。

github.com

簡単な回避策は独自定義型のほうの型名を変えてしまうことです。
例えば接頭辞として My をつけて、

// Rust

pub struct MyPath {
    b: bool,
    s: String,
}

#[derive(Tsify)]
pub struct MyRange {
    b: bool,
    s: String,
}

#[derive(Tsify)]
pub struct MyCow {
    b: bool,
    s: String,
}

#[derive(Tsify)]
pub struct A {
    path: MyPath,
    range: MyRange,
    cow: MyCow,
}

このようにすると、

// .d.ts

export interface MyPath {
    b: boolean;
    s: string;
}

export interface MyRange {
    b: boolean;
    s: string;
}

export interface MyCow {
    b: boolean;
    s: string;
}

export interface A {
    path: MyPath;
    range: MyRange;
    cow: MyCow;
}

このように独自定義の型を引用したうえで正しく型情報が生成されます。

 

まとめ

Path, Range, Cow などはユーザーが独自に型を定義するときにも型名として使ってしまいがちです。
Tsify においては予約済みの型名なので注意が必要ですね。

 

 

私からは以上です 🐄