Skip to content

Instantly share code, notes, and snippets.

@vmwxiong
Last active June 8, 2021 07:12
Show Gist options
  • Save vmwxiong/eabc60834b32801f5a38de99f4acad80 to your computer and use it in GitHub Desktop.
Save vmwxiong/eabc60834b32801f5a38de99f4acad80 to your computer and use it in GitHub Desktop.
import {
constrainAttributes,
constrainSchema,
ExposeAttributes,
} from "../../@types";
import { createScript } from "../../extensions/create-script-decorator";
import { ScriptTypeBase } from "../../extensions/script-type-base";
const attributes = constrainAttributes({
test: {
type: "string",
},
testJson: {
type: "json",
schema: constrainSchema([
{
name: "testAsset",
type: "asset",
assetType: "texture",
},
]),
},
testJsonArray: {
type: "json",
array: true,
schema: constrainSchema([
{
name: "firstName",
type: "string",
},
{
name: "age",
type: "number",
},
]),
},
testAsset: {
type: "asset",
assetType: "template",
},
});
export interface HelloWorld extends ExposeAttributes<typeof attributes> {}
@createScript()
export class HelloWorld extends ScriptTypeBase implements pc.ScriptType {
static readonly scriptName = "helloWorld";
attributes = attributes;
initialize() {
console.log(this.test);
// Should be a string
console.log(this.testJsonArray[0].firstName);
// Should be a texture
console.log(this.testJson.testAsset.resource);
// Should be a template
console.log(this.testAsset.resource);
}
}
import {
Animation,
Asset,
Color,
ContainerResource,
Curve,
Entity,
Font,
Material,
Model,
ModelComponent,
ScriptComponent,
Shader,
Sound,
Sprite,
StandardMaterial,
Template,
Texture,
Vec2,
Vec3,
Vec4,
} from "playcanvas";
/**
* Supported attribute types
*/
export type AttributeType = keyof AttributeNameMap;
export type AssetType = keyof AssetNameMap;
/**
* PlayCanvas attribute definition
*/
export interface IAttributeDef {
type: AttributeType;
name?: string;
schema?: IAttributeDef[];
default?: unknown;
title?: string;
description?: string;
placeholder?: string | string[];
array?: boolean;
size?: number;
min?: number;
max?: number;
precision?: number;
step?: number;
assetType?: AssetType;
curves?: string[];
color?: string;
enum?: Record<string, unknown>[];
}
export interface INamedAttributeDef extends IAttributeDef {
name: string;
}
/**
* Attributes collection definition when define a script class
* @exports
*/
export interface IAttributeCollection {
[key: string]: IAttributeDef;
}
// TODO: Many more
type ValueOf<T> = T[keyof T];
export interface TypedAsset<T extends ValueOf<AssetNameMap>> extends Asset {
resource: T;
}
export interface StandardModelComponent extends ModelComponent {
material: StandardMaterial;
}
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
type AssetNameMap = {
animation: Animation;
audio: Sound;
binary: ArrayBuffer;
container: ContainerResource;
cubemap: Texture;
css: string;
font: Font;
json: JSONValue;
html: string;
material: Material;
model: Model;
script: ScriptComponent;
shader: Shader;
sprite: Sprite;
template: Template;
text: string;
texture: Texture;
};
type AttributeNameMap<SchemaType = unknown, GenericAssetType = Asset> = {
boolean: boolean;
number: number;
string: string;
json: SchemaType extends readonly INamedAttributeDef[]
? JSONFromSchema<SchemaType>
: never;
asset: GenericAssetType extends keyof AssetNameMap
? TypedAsset<AssetNameMap[GenericAssetType]>
: Asset;
entity: Entity;
rgb: Color;
rgba: Color;
vec2: Vec2;
vec3: Vec3;
vec4: Vec4;
curve: Curve;
};
// Intentionally written as a distributive conditional type so that `SingleOrArray<boolean, T>` is T | T[]
type SingleOrArray<IsArray extends boolean | undefined, Type> =
IsArray extends true ? Array<Type> : Type;
/**
* Extend a class with this type and pass in its constrained attributes
* to directly map its attributes onto the class properties
*/
export type ExposeAttributes<T extends IAttributeCollection> = {
[K in keyof T]: SingleOrArray<
T[K]["array"],
AttributeNameMap<T[K]["schema"], T[K]["assetType"]>[T[K]["type"]]
>;
};
/**
* Wrap attributes with this in order to gain autocompletion and type inferences
*/
export function constrainAttributes<T extends IAttributeCollection>(t: T) {
return t;
}
/**
* Wrap schemas with this in order to gain type inferences
*/
export function constrainSchema<
N extends string,
T extends readonly (INamedAttributeDef & { name: N })[]
>(t: T) {
return t;
}
type JSONFromSchema<Schema extends ReadonlyArray<INamedAttributeDef>> = {
[Name in Schema[number]["name"]]: AttributeNameMap<
never,
Extract<Schema[number], { name: Name }>["assetType"]
>[Extract<Schema[number], { name: Name }>["type"]];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment