1

I am learning type assertion concept in typescript. After Reading tutorial offical documentation, older version and some others, I have been confused about how typescript evaluates type assertions as valid.

the following example will make my problem more clear:

I have some type in the code:


type typA = {
    name: string;
};

type typB = {
    name: string;
    age: number;
};

type typC = {
    isGraduated: boolean;
};

type typD = {
    address: string;
    age: number;
};

type typEmpty = {};

then i created some variables that have the above types:


let a: typA = {
    name: "John",
};

let b: typB = {
    name: "Alex",
    age: 10,
};

The problem happen when i run the code below:


a as typEmpty;           // (1) Ok
b as typEmpty;           // (2) Ok

a as typB;               // (3) Ok
b as typA;               // (4) Ok

b as typC;               // (5) error: ts(2352) Conversion of type '...' to type '...' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
b as typD;               // (6) error: ts(2352) Conversion of type '...' to type '...' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
1 as string              // (7) error: ts(2352) Conversion of type '...' to type '...' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

b as never as typC;      // (8)  suprisingly, Ok
b as unknown as typC;    // (9)  suprisingly, Ok
b as any as typC;        // (10) suprisingly, Ok
1 as never as string     // (11) suprisingly, Ok


I have wonder that Why (8), (9), (10) and (11) are Ok while (5),(6), (7) make error?

I have searched a lot before pushing question in here, but i have not found reasonable answers yet.

Thanks for considering my stupid question.

5
  • The compiler's job is to help prevent you from making mistakes by using available static information. #5–7 have incompatible type information on each side of the assertion. In each of #8–11, you are basically removing the type information with the first assertion (never/unknown/any completely lack structural information), then asserting again with completely new information — so the compiler has nothing to compare against — the only option is for it to essentially conclude: "If you say so…🤷". Commented Aug 18, 2024 at 13:09
  • 1
    ^ @LeoPkm2-1 When comparing (B ↔ C) or (B ↔ D) or (number ↔ string), each type lacks properties that the other type has — which makes the structures incompatible. See Type Compatibility in the TS handbook for more info. Commented Aug 18, 2024 at 14:58
  • @jsejcksn The term "overlaps" in the error notification refers to overlaps in Set concept of math? Commented Aug 18, 2024 at 15:04
  • ^ @LeoPkm2-1 Similar, but not exactly — TS is structurally-typed (vs. e.g. nominal or identity comparisons). The link I previously shared goes into detail about structural typing and compatibility. Commented Aug 18, 2024 at 15:52
  • 1
    Structural typing in TS has been extensively covered in other questions, such as this one and this one. Do either of those answer your question? Commented Aug 18, 2024 at 15:56

1 Answer 1

1

5, 6, 7 attempts to implicitly convert a type into the other, which is detected as unsafe and Typescript refuses to do this unless you clarify that you indeed want this conversion by explicitly converting the values. The error message prompts you to convert them to unknown first. This explains why (9) was OK.

Reference: https://mariusschulz.com/blog/the-unknown-type-in-typescript

and: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html

TypeScript 3.0 introduces a new top type unknown. unknown 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.

The quote above is direct quote from the documentation. Since anything is assignable to unknown, (9) is OK and it's not surprising.

Reference: https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

❌ Don’t use any as a type unless you are in the process of migrating a JavaScript project to TypeScript. The compiler effectively treats any as “please turn off type checking for this thing”. It is similar to putting an @ts-ignore comment around every usage of the variable. This can be very helpful when you are first migrating a JavaScript project to TypeScript as you can set the type for stuff you haven’t migrated yet as any, but in a full TypeScript project you are disabling type checking for any parts of your program that use it.

Therefore any turns off type checking for the variable, so (10) is OK too.

Things are not assignable to never according to the documentation:

Comparison Table

Reference: https://www.typescriptlang.org/docs/handbook/type-compatibility.html#any-unknown-object-void-undefined-null-and-never-assignability

so (8) and (11) may look surprising, but in your example types are converted to never, so maybe it makes sense after all.

Sign up to request clarification or add additional context in comments.

1 Comment

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.