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


#1

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?


#2

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
    }
}

#3

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.


#4

@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");

#5

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


#6

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


#7

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?


#8

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"