Skip to content

一些可能稍微准确、优雅一点的类型注释 #1

@Heaveeeen

Description

@Heaveeeen

基于base.ts,本来只是我自用的,但感觉可能对你们有帮助,所以随手发出来了。用品牌类型跟踪了不同意义的字符串,稍微完善了一下Sb3Input。不保证准确可用,包含一些我个人的需求和习惯,权当个参考吧。

// 基于:https://github.com/scratch-fuse/serializer/blob/main/src/base.ts
// https://en.scratch-wiki.info/wiki/Scratch_File_Format

export type Sb3BlockID<T extends Sb3Block = Sb3Block> = string & { __brand: T };
export type Sb3BroadcastID = string & { __brand: "Sb3BroadcastID" };
export type Sb3VariableID = string & { __brand: "Sb3VariableID" };
export type Sb3ListID = string & { __brand: "Sb3ListID" };

export interface Sb3Block {
    opcode: string
    next: Sb3BlockID | null // ID
    parent: Sb3BlockID | null // ID
    inputs: { [key: string]: Sb3Input }
    fields: { [key: string]: Sb3Field }
    shadow: boolean
    topLevel: boolean
    x?: number
    y?: number
    mutation?: Sb3Mutation
}

export interface Sb3Mutation {
    [key: string]: string | Sb3Mutation[]
}

// #region Sb3Input

// shadow block 就是焊死在积木里的输入框或者下拉菜单啥的,此处我简称 shadow。
// 关于 substack,C字积木嘴里的那串东西就是一个 substack,一串积木就是一个stack,一摞积木,这样的感觉。substack 就是一个子串的感觉。


/** 来源于:https://github.com/scratchfoundation/scratch-vm/blob/e5950c3de5adb4859befc6fe1b19a2142be5c192/src/serialization/sb3.js */
export const enum Sb3InputType {
    /** 未被遮盖的 shadow,即字面量输入框。  
     * unobscured shadow */
    SAME_BLOCK_SHADOW = 1,
    /** 没有 shadow,即没有输入框,底下可能是个布尔型的框,也可能这个输入是C字积木的 substack。  
     * no shadow */
    BLOCK_NO_SHADOW = 2,
    /** 底下一个 shadow 输入框,顶上塞进来一个积木把输入框盖住了。  
     * obscured shadow */
    BLOCK_SHADOW = 3,

    // Constants referring to 'primitive' blocks that are usually shadows,
    // or in the case of variables and lists, appear quite often in projects
    /** math_number */
    MATH_NUM_PRIMITIVE = 4, // there's no reason these constants can't collide
    /** math_positive_number */
    POSITIVE_NUM_PRIMITIVE = 5, // with the above, but removing duplication for clarity
    /** math_whole_number */
    WHOLE_NUM_PRIMITIVE = 6,
    /** math_integer */
    INTEGER_NUM_PRIMITIVE = 7,
    /** math_angle */
    ANGLE_NUM_PRIMITIVE = 8,
    /** colour_picker */
    COLOR_PICKER_PRIMITIVE = 9,
    /** text */
    TEXT_PRIMITIVE = 10,
    /** event_broadcast_menu */
    BROADCAST_PRIMITIVE = 11,
    /** data_variable */
    VAR_PRIMITIVE = 12,
    /** data_listcontents */
    LIST_PRIMITIVE = 13,
}

/** 数字输入框  
 * [4, value] */
export type Sb3ShadowInputNumber = [Sb3InputType.MATH_NUM_PRIMITIVE, string];
/** 正数输入框  
 * [5, value] */
export type Sb3ShadowInputPositiveNumber = [Sb3InputType.POSITIVE_NUM_PRIMITIVE, string];
/** 正整数输入框  
 * [6, value] */
export type Sb3ShadowInputPositiveInteger = [Sb3InputType.WHOLE_NUM_PRIMITIVE, string];
/** 整数输入框  
 * [7, value] */
export type Sb3ShadowInputInteger = [Sb3InputType.INTEGER_NUM_PRIMITIVE, string];
/** 角度输入框  
 * [8, value] */
export type Sb3ShadowInputAngle = [Sb3InputType.ANGLE_NUM_PRIMITIVE, string];
/** 颜色输入框,值为 "#" 开头的十六进制色值字符串  
 * [9, value] */
export type Sb3ShadowInputColor = [Sb3InputType.COLOR_PICKER_PRIMITIVE, string];
/** 文本输入框  
 * [10, value] */
export type Sb3ShadowInputSTRING = [Sb3InputType.TEXT_PRIMITIVE, string];
/** 广播输入框  
 * [11, name, id] */
export type Sb3ShadowInputBroadcast = [Sb3InputType.BROADCAST_PRIMITIVE, Sb3BroadcastID];
/** 变量输入框  
 * [12, name, id] | [12, name, id, x, y] */
export type Sb3ShadowInputVariable = [Sb3InputType.VAR_PRIMITIVE, Sb3VariableID] | [Sb3InputType.VAR_PRIMITIVE, Sb3VariableID, number, number];
/** 列表输入框  
 * [13, name, id] | [13, name, id, x, y] */
export type Sb3ShadowInputList = [Sb3InputType.LIST_PRIMITIVE, Sb3ListID] | [Sb3InputType.LIST_PRIMITIVE, Sb3ListID, number, number];

export type Sb3ShadowInput =
    Sb3ShadowInputNumber |
    Sb3ShadowInputPositiveNumber |
    Sb3ShadowInputPositiveInteger |
    Sb3ShadowInputInteger |
    Sb3ShadowInputAngle |
    Sb3ShadowInputColor |
    Sb3ShadowInputSTRING |
    Sb3ShadowInputBroadcast |
    Sb3ShadowInputVariable |
    Sb3ShadowInputList ;


/** shadow 输入,字面量或菜单  
 * [1, shadow | blockID]
 * 第二项貌似无论输入什么都会视为 shadow */
export type Sb3InputShadow = [Sb3InputType.SAME_BLOCK_SHADOW, Sb3BlockID | Sb3ShadowInput];
/** 积木输入,底下没有 shadow,可能是布尔输入,也可能是 C 嘴里的子串  
 * [2, blockID]  
 * 原则上第二项大概一般是 blockID ,但直接塞个 shadow input 进来也能用 */
export type Sb3InputBlockWithoutShadow = [Sb3InputType.BLOCK_NO_SHADOW, Sb3BlockID | Sb3ShadowInput];
/** 积木输入,底下盖着一个 shadow 输入框  
 * [3, blockID, shadow] */
export type Sb3InputBlockObscuredShadow =
    [Sb3InputType.BLOCK_SHADOW, Sb3BlockID | Sb3ShadowInput, Sb3BlockID | Sb3ShadowInput] |
    [Sb3InputType.BLOCK_SHADOW, Sb3BlockID | Sb3ShadowInput] ;

/** sc 把这玩意的行为搞得非常宽松。。  
 * 有很多人都会手动改 project.json 搞出一堆不规范但能跑的写法,应该把边缘情况都考虑进去 */
export type Sb3Input =
    Sb3InputShadow |
    Sb3InputBlockWithoutShadow |
    Sb3InputBlockObscuredShadow ;

// #endregion

export type Sb3Field = [string, Sb3BroadcastID | Sb3VariableID | Sb3ListID | null] // [value, id]

export type Sb3Workspace = Record<Sb3BlockID, Sb3Block>

export interface Sb3Target {
    isStage: boolean
    name: string
    variables: Record<
        Sb3VariableID,
        [string, string | number | boolean | (string | number | boolean)[]]
    > // id -> [name, value]
    lists: Record<Sb3ListID, [string, (string | number | boolean)[]]> // id -> [name, value]
    broadcasts: Record<Sb3BroadcastID, string> // id -> name
    blocks: Sb3Workspace
    comments: Record<string, unknown>
    currentCostume: number
    costumes: Array<{
        assetId: string
        name: string
        md5ext: string
        dataFormat: string
        rotationCenterX: number
        rotationCenterY: number
    }>
    sounds: Array<{
        assetId: string
        name: string
        md5ext: string
        dataFormat: string
        format: string
        rate: number
        sampleCount: number
    }>
    volume: number
    layerOrder: number
    tempo: number
    videoTransparency: number
    videoState: 'on' | 'off' | 'on-flipped'
    textToSpeechLanguage: string | null
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions