Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type-safe cast #3193

Closed
JonathanMEdwards opened this issue May 18, 2015 · 8 comments
Closed

type-safe cast #3193

JonathanMEdwards opened this issue May 18, 2015 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@JonathanMEdwards
Copy link

There doesn't seem to be a safe cast in Typescript. (<T> x) does no dynamic check. This is a very common need and is provided in every other optionally and gradually typed language I know of. What is needed is something like:

function cast<T>(x: any): T {
    if (x instanceof T) return x;
    throw new Error('type cast exception');
}

except that doesn't compile. Is there an alternative solution I'm missing?

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label May 18, 2015
@RyanCavanaugh
Copy link
Member

function cast<T>(instance: T, ctor: { new(...args: any[]): T }): T {
    if (instance instanceof ctor) return instance;
    throw new Error('type cast exception');
}

The reason there's no built-in operation for this is that it's not possible at runtime to detect many things (for example, (x: number) => any and (x: string) => any are indistinguishable from a runtime perspective).

@JonathanMEdwards
Copy link
Author

I think you're just saying that instanceof is broken on primitives in JavaScript. But why can't we have a cast operation that is consistent with the semantics of instanceof? Which would work as expected on classes.

@RyanCavanaugh
Copy link
Member

Can you post an example of how you'd want that to work? instanceof is already a qualifying operation for a type guard (e.g. if you have var x: Giraffe|Elephant, if(x instanceof Giraffe) { x.longNeck(); } works)

@JonathanMEdwards
Copy link
Author

Like I said:

function cast<T>(x: any): T {
    if (x instanceof T) return x;
    throw new Error('type cast exception');
}

I find I keep repeating that boilerplate which mimics a "normal" safe cast operation.

@RyanCavanaugh
Copy link
Member

For example, let's say you wrote the following:

interface Foo {
  (x: string): void;
}

var j: any = whatever;
var x = cast<Foo>(j);
/* write the JavaScript you would expect to be emitted here */

The cast function that can exist is the one I wrote above -- you have to specify the constructor function, but the types work as expected and the runtime behavior is correct.

@JonathanMEdwards
Copy link
Author

OK. I'd expect that to be an error, but that certainly would be annoying for a supposed cast expression.
How does your suggestion work? This doesn't:

function cast<T>(instance: T, ctor: { new(...args: any[]): T }): T {
    if (instance instanceof ctor) return instance;
    throw new Error('type cast exception');
}
class A {
    foo: number;
}
var x: A | number = new A();
var y:A = cast<A>(x, A);

test.ts(9,19): error TS2345: Argument of type 'number | A' is not assignable to parameter of type 'A'.
Type 'number' is not assignable to type 'A'.

@duanyao
Copy link

duanyao commented May 18, 2015

This works:

function cast<T>(instance, ctor: { new(...args: any[]): T }): T { // instance should be "any"
    if (instance instanceof ctor) return instance;
    throw new Error('type cast exception');
}
class A {
    foo: number;
}
var x: A | number = new A();
var y = cast(x, A); // type param is not required

@JonathanMEdwards
Copy link
Author

Thanks @duanyao that's exactly what I was looking for!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

3 participants