Change keyword name: 'any' to 'unknown'

Topics: Language Specification
Jul 16, 2014 at 11:38 AM
Edited Jul 16, 2014 at 11:41 AM
Some recent posts (#2622, #2624), or tweets, were talking about the type any, in which the developers were using it 'wrong', or 'not as intended', or... anyway.

@danquirk said in #2624:
A value of type 'any' is simply assignable to or from a value of any type. That assignability rule is precisely why the type exists. It is to model how JavaScript works.
Understandable. Any normal JavaScript variables should have any type because we don't know their exact type. However, the type name "any" itself is somewhat misleading.
interface Foo {
    a: any;
This interface has a property a, which type is any. Here, a programmer would expect that they can assign any types of values to the property.
interface Foo extends Bar {
    a: any;
interface Bar {
    a: number
In this example, we now have a Bar interface that has a number type variable, and Foo now extends Bar. That is, every objects that is compatible with Foo should be compatible with Bar. As you know, this code would not match that criteria because a property in interface Foo would not be always compatible with the one in interface Bar. However, this anyway is valid code, because the any type is designed so.
var bar: Bar = { a: 3 }; // Valid
var foo: Foo = { a: 'not a number' }; // Valid
var foobar: Bar = <Foo>{ a: "I'm a string" }; // Valid... wait, really?
The value assigned to foobar is not really compatible with interface Bar, but this is valid TypeScript code, because a value of type 'any' is simply assignable to or from a value of any type!

Fortunately, we have a way to prevent this.
interface Foo extends Bar {
    a: {};
interface Bar {
    a: number
Now the type checker will report that the property a in interface Foo has an incompatible type "{}", because type {} does not have the same assignability rule that the type any have.

What's the point? I would say that the existing type name any should be changed to unknown, and the new type any should be an alias for the type {}. (The name any is anyway better than {});

Let's note that W3C WebIDL also have their own any type. Its any type is defined as the union of all other possible non-union types. It works as the type {} does (while TypeScript has no union type yet). This type is used to specify the parameter type of standard APIs. That is, the name any is/will be known to many, many Web developers. It should not be misleading because it would frustrate those developers who may try TypeScript later.

I suggest the new any/unknown type, so that we can give the type any to literally assign any values of any types, but without any specialized assignability rule designed for JavaScript.
Jul 16, 2014 at 5:42 PM
One thing to keep in mind is that names are not selected by random "just because". Those behind TypeScript are focusing the semantics on what lays ahead for the future of JavaScript. One must always ask first "is this in TS because it's in ES6/7?", because if it is, it's not going to change. I found this link here on that references the "any" type as well:
Jul 16, 2014 at 8:05 PM
@jamesnw Current type {} also can reference any ECMAScript value. Would there a problem if we define any type as an alias for type {}?
Jul 16, 2014 at 8:50 PM
Edited Jul 16, 2014 at 8:52 PM
Yes, {} means "an empty object", which matches "all objects" as a base type.
var o1: {} = {x: 1}; // ok
var o2: {x} = <{}>{x: 1}; // error
o1 is not type "any", its a type of "empty object" (a base type for all types only), which is why this also fails: o1.x = 1; // error: The property 'x' does not exist on value of type '{}'.

If o1 was of of type 'any', then o1.x = 1; would work.

Let's try something else:
var n1: number = 1;
var n2: {} = 2;
n1 = n2; // fails: Cannot convert '{}' to 'number'.
So, though you can assign anything to n2, what use is it? You can't assign it to anything else other than another {}, Object, or any type only.
Jul 16, 2014 at 9:28 PM
Edited Jul 16, 2014 at 9:55 PM
That consequence is exactly what I want.
var param: any = arg1; // an unexpectable value from external source
var num: number = param;
Current any type just allows this. Why it should be allowed in TypeScript? I think that the only reason is to guarantee the compatibility with regular JavaScript. TypeScript supports type casting which is more strict, and we can do this:
var param: {} = arg1; // an unexpectable value from external source
var num: number = <number>param; // Valid casting
We can explicitly cast the type so that we can always assure what's going on.
Jul 16, 2014 at 9:38 PM
Edited Jul 16, 2014 at 9:39 PM
What I want is:

current type any -> new type unknown
current type {} -> make an alias for it with new any so that any refers {}

Then, type-less ordinary JavaScript variables would have new type unknown which is same as current any, and new any would lose its specialized assignability rule while unknown still have it.
Jul 16, 2014 at 11:03 PM
Well, in either case, "unknown" is not a good alternative IMHO. Sometimes a type IS known, just cast in a special way, like this:
class A { static n: number; }
var callConstructorA: {():A} = <any>A; // <unkown>A; is not right, because I know it.
I've actually done something similar in a project already (though this is a special case).

I really doubt this will change. There are tons of developers with projects that have over 150 scripts each, and they are not going to break it because one person doesn't want to type {}, and prefers any to personally mean something else. ;) Best you can hope for is type alias support, like here:
Jul 17, 2014 at 2:42 AM
A couple things:

Changing 'any' to mean {} would break essentially every piece of existing TypeScript out there. We would never do this.

Second, I think you're misreading how the WebIDL is describing 'any'. What it describes is basically what TypeScript models with 'any'. It says
The any type is like a discriminated union type, in that each of its values has a specific non-any type associated with it. For example, one value of the any type is the unsigned long 150, while another is the long 150. These are distinct values. 
A discriminated union essentially defines a list of types and an instance of the discriminated union type can only be a single one of those types. Generally you would need to handle all of its possible states in order to have a provably sound program. For a more concrete example, in F# you could define a discriminated union type like type MyThings = | Foo | Bar and now when processing a value x of type MyThing the compiler will give you an error if you do not explicitly handle the case where x is of type Foo and the case where x is of type Bar. In our case here the type Any is a discriminated union representing not just the Foo and Bar types, but ALL types that could ever exist. Obviously it's impossible to ask someone to explicitly handle how to process a value for each individual type it could ever be. Instead the compiler does no checking and leaves you free to handle it as you see fit as this is how JavaScript manages this type of world.

{} means something very precise and very different. It means an object with no members (other than those inherited from Object). It is almost completely the opposite of 'any' in a lot of cases. You can assign a value of type 'any' to anything. You can assign a value of type {} to nothing except {}, Object and any. Defining something as type {} makes that value almost unusable as a result. At almost every point where you use an object of type {} you would need to cast it to a more specific type to do something as simple as property access or assignment. This is surely not what people want to do.
Jul 17, 2014 at 9:49 AM
@jamesnw, Um... I think this is better to do that.
class A { static n: number; }
var callConstructorA: { new (): A } = A;
Now you don't have to do any type cast, while the resulting compiled JavaScript is same.

And... the name unknown can be thought as:
enum KeyboardEventNames {
    KeyDown = <unknown>"keydown", // OK, you know that this is a string, but let's "forget" it and treat it as number.
    KeyUp = <unknown>"keyup"
... though this is really tricky case.

The name dynamic may be more friendly for some C# users, as user @diullei say.

@danquirk, Yes... I don't know much about F# and discriminated union. Thank you for your explanation.

However, it seems that F# have "pattern matching" to work type-safely.
type Variant =
    | Numeric of int
    | Text of string
    | Empty
let print v =
    match v with
    | Numeric n -> printfn "Num %d" n
    | Text s    -> printfn "Txt %s" s
    | Empty     -> printfn "Empty"
TypeScript users also would want to know the exact type of any-typed objects and then use it, if they want to be type-safe. What do we have? We have type-casting, and we still can check the type somehow.
//A pre-defined function
//function getResultFromSomething(): any {
//  if (someoneAsksTheAnswer)
//      return 42;
//  else if (anywaySomethingHappens)
//      return "the result I want"

var result: string;
result = getResultFromSomething();
    Hmm... I'm new to TypeScript.
    I assigned a value to string typed variable and type checker does not show any error.
    The data should be a string here.
    I can safely keep going...
Instead, one would do this:
var result: string;
//result = getResultFromSomething();
Oh, My IDE says I cannot do an implicit conversion here! 
Something is wrong.
Oh, is not a string type property. Thank you type checker.

result = <string>getResultFromSomething();
if (typeof result !== 'string') // I have to check this to be type-safe
    throw new Error("D'oh! the result was not a string type.");
... while it would be more complex when the return value type is not a primitive one. It's JavaScript anyway...

As a former C# user, I want any to be more strict. Lot's of anys are in TypeScript, but I would not use that much dynamics in my C# code. People who want strict types would be confused, as Mr. Pastorelli was. We definitely need the special type for the compatibility with JavaScript, but we can do better in type-safe TypeScript zone, can't we?
Jul 17, 2014 at 2:53 PM
@SaschaNaz Sorry, that code is not what the example is trying to do. The point was to call the constructor directly without "new".
Jul 17, 2014 at 6:42 PM
Edited Jul 18, 2014 at 3:39 PM
I don't think that argument holds. Just because you force cast something doesn't mean it will be enforced at runtime, so like in your example, you have to check it anyhow. This is true in all cases, since in the JavaScript world, you could get something other than string. Consider this:
function doSomething(s: string) { }
So, when end users use that API function (in JavaScript, not TypeScript), can I guarantee that 's' is a string? Of course not. I need to check it anyway. This is the nature of JavaScript. TypeScript is not a new language, it just adds type information to the existing JavaScript behavior. Adding a cast from 'any' doesn't solve the problem, except forcing developers to cast to the type the variable already is. When I add functions, I always check the return type. If a user wanted to call "getResultFromSomething()", I think they'd be pretty dumb not to also know what it does, and check the return type (unless they're a monkey just hitting keys). In the case for user callback functions with return types, even if the return type is "string", that means nothing, because in the JS world, it can be anything, so I'd still have to check it. When I was new to TypeScript back near the beginning, this was never ever an issue at all. Perhaps this is more an issue for those coming from C#.

Don't compare with F# or C#, because this is JavaScript, and behaves very different. For those coming from JavaScript, any type checking is many times better than none at all. (for the record, though I know dozens of languages [and even created some of my own scripting languages], and created many JS libraries for personal use, I recently came into TypeScript from C#/Silverlight myself, and was perfectly happy).
Jul 17, 2014 at 7:29 PM
As a current TypeScript user, I want "any" to carry on working as documented in the TypeScript specification. I've got tens of thousands of lines of production code in this language, and you want to stop them compiling because the semantics don't make sense to you?

Jul 19, 2014 at 12:06 PM
Edited Jul 19, 2014 at 12:15 PM
@jamesnw, right, we always have to check it when we make end-point API. However, we don't always make end-point APIs ;)

Hmm... The reason why I started this discussion is because I feel fairly odd about this code. (already in the post)
interface Foo {
    x: string;
interface Bar extends {
    x: any;
Interface Bar here is definitely broader than interface Foo. However, this is anyway valid code, because that's what any is. People who use any just to assign any types of values also have to deal with the special rule, which allows any-type value to be assigned to any non-any type variables.

The main point of my last example was not about the manual type checking that we actually always have to do when we make JavaScript APIs. I just wanted to describe some scenarios that more strict any might be more helpful. It seems that it was unsuccessful.

Well... when I wrote my last example, I found that my suggestion would be really painful when we want to do this.
function foo(x: any) {
    if (x.specificProperty) { // Let's check the type by checking a certain property is there
    else if (x.otherProperty) { // Checking another specific type

We have to cast all the time to do simple things if this is prevented, as @danquirk said. Definitely we don't want to do this.
function foo(x: any) {
    if ((<SomeSpecificType>x).specificProperty) { // Fairly painful... while someone may prefer this
    else if ((<AnotherSpecificType>x).otherProperty) {

Well, I'm not an evil trying to break tens of thousands of lines of production code in this language. Then, I thought about the less-strict any than my first suggestion.
// Declaring variables
var a; // implicit unknown
var a2: unknown; // explicit unknown
var b: any; // new any
var c: {} // {}

// Accessing properties
a.x // allowed
b.x // allowed
c.x // not allowed

// Assigning
var foo: string;
foo = a; // allowed
foo = b; // not allowed. Hey, are you sure you know what you are doing? If so, consider a type casting ;)
foo = <unknown>b; // allowed
foo = c; // not allowed

// Extending interfaces
interface Foo {
    x: string;
interface A extends Foo {
    x: unknown; // allowed
interface B extends Foo {
    x: any; // not allowed. B is broader than Foo here
interface C extends Foo {
    x: {}; // not allowed
  1. Unknown, or maybe dynamic, here is exactly the same one as current any. It still have the special assignability rule.
  2. Any now loses the rule but we can still access its properties without any type casting.
  3. {} is the {} we know.
This still will break existing codes, but that would not be as severe as my first suggestion. Maybe we can add a compiler option that does nothing to any but just adds unknown as an alias for any, to protect existing codes.

Would this still be unacceptable and break all tens of thousands of lines of production code in this language so drastically? Please note that I just want to prevent any possible misleading and confusion. I like TypeScript, and I don't want to be evil but rather just discuss this.