TypeScript 3.0 introduces unknown
type, according to their wiki:
unknown is now a reserved type name, as it is now a built-in type. Depending on your intended use of unknown, you may want to remove the declaration entirely (favoring the newly introduced unknown type), or rename it to something else.
What is difference between unknown
and any
? When should we use unknown
over any
?
Anything is assignable to unknown, but unknown isn't assignable to anything but itself
sounds like rhyme
You can read more about unknown
in the PR or the RC announcement, but the gist of it is:
[..] unknown which is the type-safe counterpart of any. Anything is assignable to unknown, but unknown isn't assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.
A few examples:
let vAny: any = 10; // We can assign anything to any
let vUnknown: unknown = 10; // We can assign anything to unknown just like any
let s1: string = vAny; // Any is assignable to anything
let s2: string = vUnknown; // Invalid; we can't assign vUnknown to any other type (without an explicit assertion)
vAny.method(); // Ok; anything goes with any
vUnknown.method(); // Not ok; we don't know anything about this variable
The suggested usage is:
There are often times where we want to describe the least-capable type in TypeScript. This is useful for APIs that want to signal “this can be any value, so you must perform some type of checking before you use it”. This forces users to safely introspect returned values.
The difference between unknown and any is described as:
Much like any, any value is assignable to unknown; however, unlike any, you cannot access any properties on values with the type unknown, nor can you call/construct them. Furthermore, values of type unknown can only be assigned to unknown or any.
To answer your question of when should you use unknown
over any
:
This is useful for APIs that want to signal “this can be any value, so you must perform some type of checking before you use it”. This forces users to safely introspect returned values.
Take a look at the TypeScript 3.0 announcement for examples of type checking a variable of type unknown
and a more detailed explanation.
unknown
, by "you" here you mean me or the ones who consume my function? Who have to do the type check?
any type:
The any
type represents all possible JS values. Every type is assignable to type any
. Therefore the type any
is an universal supertype of the type system. The TS compiler will allow any operation on values typed any
. For example:
let myVar: any;
myVar[0];
myVar();
myVar.length;
new myVar();
In many occasions this is too lenient of the TS compiler. i.e. it will allow operations which we could have known to be resulting into a runtime error.
unknown type:
The unknown
type represents (just like any
) all possible JS values. Every type is assignable to type unknown
. Therefore the type unknown
is another universal supertype of the type system (alongside any
). However, the TS compiler won't allow any operation on values typed unknown
. Furthermore, the unknown
type is only assignable to the type any
. An example will clarify this:
let myVar: unknown;
let myVar1: unknown = myVar; // No error
let myVar2: any = myVar; // No error
let myVar3: boolean = myVar; // Type 'unknown' is not assignable to type 'boolean'
// The following operations on myVar all give the error:
// Object is of type 'unknown'
myVar[0];
myVar();
myVar.length;
new myVar();
any, unknown:
allow assigning any type
any:
allows being assigned to any type
allows calling any method
unknown:
doesn't allow being assigned to any type
doesn't allow calling any method
const a: any = 'a'; // OK
const b: unknown = 'b' // OK
const v1: string = a; // OK
const v2: string = b; // ERROR
const v3: string = b as string; // OK
a.trim() // OK
b.trim() // ERROR
doesn't allow to call any method
, this is incorrect (or maybe just hard to understand?). anyway, you can call any method on a value typed as any
.
as T
, which will change the type from unknown
to T
. Very good.
any
is basically using bare javascript. unknown
is basically a safer way to deal with stuff you don't know the type.
they are different in semantics.
unknown
is the parent type of all other types. it's a regular type in the type system.
any
means "turn off the type check". it's kind of meta programming.
any
?
Unknown
If you write a function that only passes down an input to another function, use unknown
. From the perspective of the function: "I don't know, I don't wanna know". There is nothing wrong with using unknown
.
E.g.:
function buy(item: unknown): Purchase {
if (item) {
return purchase(item);
} else {
throw new TypeError('item is missing');
}
}
Any
If you need to call properties on that value, then any
is more suited.
Linting might not like any
, suggesting you to be more specific with your input. That way, if you change the interface from isItem
to isValid
, typescript tells you to update your code.
E.g.:
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function isItem(item: any): item is Purchase {
return !!item?.price;
}
Calling properties
function isStuff(item: unknown): item is Stuff {
return (item as Stuff).it !== undefined;
}
function isStuff(item: any): item is Stuff {
return item.it !== undefined;
}
camelcaseKeys(item) as unknown as Item;
See user defined guards if you're interested, I brought it in because it's one of the few cases where I need any.
From this blog from ultimatecourses:
Use the any type when there are no other options
It's hard to find good examples for any
.
I am late to the party but will try to demystify it.
const canBeAnything: any = 100;
const canNotBeAnything: unknown = 100;
// If we try to use a .startsWith() method
canBeAnything.startsWith('10'); // no error
canNotBeAnything.startsWith('10'); // Property 'startsWith' does not exist on type 'unknown'
only way to use the method .startsWith()
on unknown is explicitly telling compiler the type, like
(canNotBeAnything as string).startsWith('10'); // Chill down TS compiler, I know what I am doing.
The latter on doesn't show any compilation error but it throws error during runtime because canNotBeAnything
is a number type and we are forcing it to be string
The accepted answer says "unknown which is the type-safe counterpart of any."
However, as this example shows unknown
is its own beast and it sometimes behaves very differently from any
:
type Foo = unknown extends string ? true : false // false
type Bar = any extends string ? true : false // boolean - i.e. both true and false
Success story sharing
any
is likedynamic
andunknown
is likeobject
. I likeunknown
as it is just more type safe. Confusing names though.unknown
andobject
@nawfal although I think I see what you're getting at in terms of the contravariance and covariance (e.g. any object is assignable to typeobject
although forunknown
any object or primitive may be assigned - similarlyunknown
can be assigned toany
or itself andobject
could be assigned todynamic
or itself). On a side note I'm unclear why the TS docs refer tounknown
as a top type because it doesn't really contain all types ¯_(ツ)_/¯unknown
is I don't know;any
is I don't careunknown
is I don't know (yet), thus I have to figure it out,any
is I don't care, thus I don't careunknown
, your code has to identify a type before you can access any members of that type or the TS compiler will complain; with typeany
, the compiler will not complain about accessing a property, even though that property may not exist at runtime?