How to use scripts and attributes in a typescript project?

Can someone please provide additional examples of how to use scripts and attributes in a typescript project.

const customScript = PlayCanvas.createScript('customScript') results in an object that has a PlayCanvas.ScriptAttributes property and throws errors that customScript.prototype.initialize/update does not exist, but can be circumvented with ts-expect-error. The ScriptAttributes get method only returns an object that has the default value. The attribute values passed in the script.create() method do not seem to be injected into the object.


Update 2026-04-16

One error I had was ScriptType cannot be invoked without 'new', which was a typescript issue where I had to change the tsconfig target from es5 to es6.

I was able to get this first technique to work

const customScript = PlayCanvas.createScript('customScript');

// works - speed is accessible and assigned default value of 3000
customScript?.attributes.add('speed', { type: 'number', default: 3000 });

if(customScript) {
    // @ts-expect-error - works at runtime (use function, not arrow function)
    customScript.prototype.initialize = function () {
        console.log('script initialized');
    }
    // @ts-expect-error - works at runtime (use function, not arrow function)
    customScript.prototype.update = function (dt: number) {
        // @ts-expect-error - speed gets printed at runtime
        console.log('speed: ' + this.speed);
    }
}

customEntity.addComponent('script');
if(customEntity.script) {
    const scriptt = customEntity.script.create('customScript', {
        // works - speed gets set to 3001
        attributes: {
            speed: 3001
        }
    });

    // @ts-expect-error - works - speed gets set to 3002
    scriptt.speed = 3002;
}

Update

I was able to get the second technique to work

export class CustomScript extends PlayCanvas.ScriptType {
	static scriptName = "customScript"

	speed: number = 3000;

	public initialize() {
		console.log('CustomScript::initialize()');
	}

	public update(dt: number) {
		console.log(`CustomScript::update(): ${this.speed}`);
	}
}

{
    PlayCanvas.registerScript(CustomScript, 'customScript');

    // Default value assigned in class.
    // This does not overwrite value initialized in class.
    // CustomScript.attributes.add('speed', { type: 'number', default: 3000 });

    CustomScript.attributes.add('speed', { type: 'number' });

    customEntity.addComponent('script');
    const scriptt = customEntity.script!.create(CustomScript, {
        attributes: {
            // works - speed gets set to 3001
            speed: 3001
        },
    });

    // @ts-expect-error - works - speed gets set to 3002
    scriptt.speed = 3002;

    const customScript = customEntity.findScripts('customScript')[0];
    // @ts-expect-error - works - speed gets set to 3003
    customScript.speed = 3003;
}