How to cast to a type in TS to get rid of errors

I’m using the TS types (put together by Pablo) and with the help of VS code it is showing the errors but it is also showing errors when using base types.

[ts] Type ‘SceneNode’ is missing the following properties from type ‘GraphicsNode’: fill, fillEnabled, stroke, strokeEnabled, and 11 more. [2740]

So if my function accepts SceneNode but has code to handle sub classes like GraphicsNode and Rectangle and when I reference strokeEnabled it shows an error. That’s expected since SceneNode doesn’t have that property.

Initial code accepts SceneNode:

/**
 * Adds a transform to a SceneNode
 * @param {SceneNode} item 
 * @param {Object} transform 
 */
function transformSceneNode(item, transform) {

	 if (item.strokeEnabled) { // warning here
		
	 }
}

So I added in the base class that has that property:

Accepts SceneNodes and GraphicsNodes code:

/**
 * Adds a transform to a SceneNode
 * @param {SceneNode|GraphicsNode} item 
 * @param {Object} transform 
 */
function transformSceneNode(item, transform) {

	 if (item.strokeEnabled) { // warning still here
		
	 }
}

Error:

Property ‘strokeEnabled’ does not exist on type ‘GraphicsNode | SceneNode’.
Property ‘strokeEnabled’ does not exist on type ‘SceneNode’. [2339]

I checked the types and it says GraphicsNode strokeEnabled proeprty.

So I finally attempted to cast to type and use the type.

Accepts SceneNodes and GraphicsNodes and assigns to desired type:

/**
 * Adds a transform to a SceneNode
 * @param {SceneNode|GraphicsNode} item 
 * @param {Object} transform 
 */
function transformSceneNode(item, transform) {
    /** @type {GraphicsNode} */
    var graphicsNode = item; // warning here

	 if (graphicsNode.strokeEnabled) {
		
	 }
}

And I get this warning:

Type ‘GraphicsNode | SceneNode’ is not assignable to type ‘GraphicsNode’.

So then I use the as operator like this:

Casts SceneNodes and GraphicsNodes using as to GraphicsNode type:

/**
 * Adds a transform to a SceneNode
 * @param {SceneNode|GraphicsNode} item 
 * @param {Object} transform 
 */
function transformSceneNode(item, transform) {
    /** @type {GraphicsNode} */
    var graphicsNode = item as GraphicsNode; // warning here

	 if (graphicsNode && graphicsNode.strokeEnabled) {
		
	 }
}

But that gives this error:

[ts] ‘type assertion expressions’ can only be used in a .ts file. [8016]
[ts] Cannot find name ‘GraphicsNode’. [2304]

So finally I am doing this:

Accepts SceneNodes and GraphicsNodes and sets variable only if instance of GraphicsNode:

/**
 * Adds a transform to a SceneNode
 * @param {SceneNode} item 
 * @param {Object} transform 
 */
function transformSceneNode(item, transform) {
    /** @type {GraphicsNode} */
    var graphicsNode = item instanceof GraphicsNode ? item : null;

    if (graphicsNode && graphicsNode.strokeEnabled) {
		
    }
}

I’m new to ES6 and have not used TypeScript. Is there a better way to handle the use cases above?

If I understand the above correctly, you can usually “guard” against a type when using TypeScript, and it will be happy. For example:

const { SceneNode, GraphicsNode } = require("scenegraph");
function transformSceneNode(item: SceneNode, transform: Object) {
    if (item instanceof GraphicsNode && item.strokeEnabled) {
        // shouldn't throw any TS errors
    }
}

I think the problem might be that you have a typo – it’s GraphicNode, not “GraphicsNode.” The error “Cannot find name” is a good red flag that something in your type annotations isn’t lining up with the APIs right.

@kerrishotts I can use that. There’s no error message.
@peterflynn I’ve been using these type definitions here.

If there’s no GraphicsNode type should there be a warning when importing from the scenegraph? Here’s what I have with no errors:

const {Artboard, BooleanGroup, Matrix, Color, Ellipse, GraphicsNode, Group, Line, LinkedGraphic, Path, Rectangle, RepeatGrid, RootNode, SceneNode, SymbolInstance, Text} = require("scenegraph");

Oops, seems like something went wrong in the early stages of developing the typings on my side. Fixing it right now…

1 Like

It’s fixed now, you can download the new version of scenegraph.d.ts from either the typings repo or the boilerplate repo :wink:

1 Like

Guarding against it doesn’t seem to work in the following case.

Here’s the original code:

/** @type {Artboard} */
var firstArtboard = numberOfArtboards>0 ? artboards.at(0) : null;

[ts] Type ‘SceneNode’ is not assignable to type ‘Artboard’. [2322]

Here’s the updated code:

if (artboards.at(0) instanceof Artboard) {
   /** @type {Artboard} */
   var firstArtboard = artboards.at(0);
}

…and I’m getting the same error.

Does ES6 need an as keyword?

If there’s no GraphicsNode type should there be a warning when importing from the scenegraph?

Unfortunately, JavaScript as a language doesn’t really allow for that sort of rigid identifier enforcement. For example:

var emptyObject = {};
var objectMember = emptyObject.foo;  // no error
console.log(objectMember);  // "undefined"

You’re getting the error because the type-guard doesn’t work that way, unfortunately. The language service behind the syntax checking isn’t “smart” enough to understand that your two calls to at(0) will return the exact same object. To work around this, do something like the following:

/**
 * @type {SceneNode}
 */
let firstArtboard = artboards.at(0);

if (firstArtboard instanceof Artboard) 
{
    // Access Artboard properties without issue!
    console.log(firstArtboard.width);
}

Hope this helps!

1 Like