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

Introduce support for checked casting #3314

Closed
jhlange opened this issue May 30, 2015 · 6 comments
Closed

Introduce support for checked casting #3314

jhlange opened this issue May 30, 2015 · 6 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@jhlange
Copy link

jhlange commented May 30, 2015

Typescript is an excellent method of building type checking into compile time validations against javascript. Unfortunately, because the result of this doesn't travel into the runtime (as javascript is basically typeless), there is 0 guarantee that what an IDE is seeing is actually the type that typescript is asserting in the language. A basic level of runtime validation could be introduced to block patterns that would be illegal in strongly typed languages.

One big area is casting. Casting to an interface or class does not do any schema validation.-- That is probably fine in many cases, but introduces a number of risks, especially for code running on servers. Casting isn't considered a security feature, but consider this type safety issue that could cause a javascript crash/exception due to a buggy program or specially crafted input

consider a Node.JS/express server that has a method call expecting a json object, which the implementor defined in a typescript definition:

interface SalesLine {
  itemName: string;
  itemQuantity: number;
}

interface  SalesOrder {
  salesLines: SalesLine[];
  createdBy: string;
}

When a new sales order is sent to the server, a user will write some code to process it, unfortunately, in it is quite easy to build the service without executing a real schema validator.-- In cases where you do have validation, mistakes can still be made internally elsewhere in the server.

Even basic code could be DoS'd or crash quite easily.-- This is a huge weakness of dynamically typed languages in general with the loss of compile time validation, but typescript could help fix some of these gaps.-- This wouldn't be a full json schema validation.-- This would simply be assignment type checking.

Strongly typed languages with ahead-of-time validations would guarantee types like references to objects are either set to null or a instance of that object.-- Arrays would be arrays or null, scalar values like integers would always be set to a valid integer, etc.

Consider this case:

The web server is sent this JSON object:

{
  salesLines: { length: 99999 }
  createdBy: { length: 2 }
}

A piece of code that doesn't run json schema validation or hand-code error-prone type checking logic could fall into a number of traps that would not arise in a pre-compiled language.

getNumberOfItems(order: SalesOrder) {
   for (var i = 0; i < order.salesLines.length; i++) ........
}

onPostRequest(req: express.Request, res: express.Response) {
  var salesOrder = <SalesOrder>req.body; // No error but the types clearly do not match what is being used
  var count = getNumberOfItems(salesOrder);
}

A very basic set of opt-in validations could easily be generated by typescript to prevent this, the idea is that checked types would run the validations when they are casted to more concrete types any -> SalesOrder, etc., consider if a 'checked' keyword was introduced.-- It could either operate similarly to the dynamic_cast in c++, or a throwing based cast like in .NET/Java/etc.--

checked interface SalesLine {
  itemName: string;
  itemQuantity: number;
}

checked interface  SalesOrder {
  salesLines: SalesLine[];
  createdBy: string;
  notes?: string;
}

In the above example the typescript would generate validation methods on the types. Since sales line is also checked, the validation functions would be recursively called:

SalesOrder.__checkedCast(o) {
    if (!(o.salesLines === null)) {
        if (typeof o.salesLines === null && !Array.isArray(o.salesLines)) return null;
        for (var i = 0; i < o.salesLines.length; i++) {
            var item = o.salesLines[i];
            if (!(item === null)) {
               var casted = SalesLine.__chekedCast(item);
               if (!(typeof casted === 'object') return null;
            }
        }
    }

    if (!(o.createdBy === null) || !(typeof o.createdBy === 'string')) return null;
     if (!(o.notes === null) || (!(typeof o.notes === 'string') || !(typeof o.notes === 'undefined'))) return null;
    return o;
}

The code generated at casting time would then look like this:

Typescript

onPostRequest(req: express.Request, res: express.Response) {
  var salesOrder = <SalesOrder>req.body;
  var count = getNumberOfItems(salesOrder);
}

Javascript

onPostRequest(req, res) {
  var salesOrder = SalesOrder.__checkedCast(req.body);
  var count = getNumberOfItems(salesOrder);
}

Checked types would be primarily for directed graphs of objects. In the future WeakMaps could be used for graphs with cycles.

@jhlange jhlange changed the title Introduce support for checked casting casting Introduce support for checked castin May 30, 2015
@jhlange jhlange changed the title Introduce support for checked castin Introduce support for checked casting May 30, 2015
@RyanCavanaugh
Copy link
Member

Any difference between this and #2444?

@jhlange
Copy link
Author

jhlange commented Jun 1, 2015

It is different, actually.-- In this example it checks to make sure the objects conforms to an interface it is being used as (conforms to typescripts duck typing style) at runtime (An assumption that the person who is using the interface is assuming).-- It is not checking to see if the object is actually a certain specific type under the javascript type system, which is a very shaky concept anyway.

@duanyao
Copy link

duanyao commented Jun 2, 2015

It seems there was an effort to introduce runtime checks to TS, called "Safer TypeScript":
Safe & Efficient Gradual Typing for TypeScript
Safer TypeScript download

However the development seems discontinued?

@mhegazy
Copy link
Contributor

mhegazy commented Jun 10, 2015

Nothing prevents using the cast explicitly today, so in a sense this is not blocked on the compiler, it is just more convenient.

As @RyanCavanaugh mentioned in #2444 (comment) run time type information is something that we have considered out of scope. this might change in the future though.

@mhegazy mhegazy closed this as completed Jun 10, 2015
@mhegazy mhegazy added Suggestion An idea for TypeScript Out of Scope This idea sits outside of the TypeScript language design constraints labels Jun 10, 2015
@jhlange
Copy link
Author

jhlange commented Jun 10, 2015

@mhegazy This isn't quite the same.-- It has nothing to do with the js type system, it is more about conformance to an interface/declaration.-- It would greatly simplify programs if it were supported, eliminating many error-prone conformance style checks like if(typeof myObject.x === 'string'.....) all over the place.-- The result is cleaner, less buggy code.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 10, 2015

@jhlange, I get your point; I am not saying there is no use case for this; i just saying this is out of the scope of the project at this point. So far Typescript gives you a desigtime-only erasable type system, with code looking almost identical to your source code (not even reordered based on declaration dependency) and no dependency on runtime libraries (just small helpers like __extends). Going down the rtti/rtts route would be a departure from that path. I would say this is something that you and/or other interested contributors can experiment with in a fork or as a pre-emit plugin.

What you are asking for is a runtime type assertion system, be it with auto generated instead of capturing the structure and using a runtime library; I undrtstand you are only talking about type cast, but you would want to do it for unsafe assignments (i.e. from an any to a non-any) as well as function calls.

A new feature that we have added recently is user defined type guards; you should be able to use that partially achieve parts of this proposal, modulo the auto generation :)

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants