Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Runtime typesafety in typescript

I was trying to better understand the type safety in Typescript and came across this scenario where I have this function:

function test(x: number){
    console.log(typeof x);
}

If I call this method this way - test('1') It throws compile time error but it works fine if I change it to this:

let y: any = '1';
test(y);
//works fine
//Outputs "string"

From what I understand is that declaring 'x' as number only works during compile time and that Typescript only enforces compile time typesafety not runtime. So, I wanted to know if I understand this correctly or missing anything and also, what all are the different ways to ensure runtime typesafety?

like image 699
Sachin Parashar Avatar asked Dec 23 '22 20:12

Sachin Parashar


1 Answers

TypeScript is two merged languages:

  1. Type level language
  2. Value level language

The first is visible as all type annotations, it is a syntax which not exists in pure JavaScript. Every type annotation and reserved words like type, interface, enum, as, in are part of type level language. What TS is doing during compilation as first is checking correctness of grammar and syntax of type level language and correctness of annotations over value level language.

The second is value level language, it is fully correct JS syntax. It also has most of features from ECMAScript proposals stage 3.

The first part is fully removed (exception is Enum which has representation in the runtime), the second stays in the runtime.

Back to the question about safety, yes TypeScript ensure safety during writing the code. You define the contract, write transformations of the contract, and TS is checking correctness of the code with relation to contract annotations. It removes a whole bunch of errors like typos, or using null/undefined objects methods and properties, it also gives visible definitions of the data during the program flow.

But, it does not secure the runtime. All type annotations are only assumptions, if we define API to have such and such structure of the end point response, then TS will guarantee that code will follow this structure, but if in the runtime, the structure will be different, the program will naturally fail, as contract not equals the data.

Back to your example

function test(x: number){
    console.log(typeof x);
}

Defining function test as a function which takes number as an argument, you are saying nothing different then number will be passed to the function. So the above implementation is really a constant, as typeof x will always return number, because that exactly what annotations says.

// below is equal to your implementation, as number has one specific type
// caution is equal for TS, not for the runtime!
function test() {
  return console.log('number')
}

If the function is polymorphic in terms of the input, then input should be annotated as such. It can happen that you don't know what input you can get, then you can implement checking of the structure. The proper name for that is - type guard. Consider below example

function test(x: unknown) {
  if (typeof x === 'number') {
    return x * 2; // return number
  }
  if (typeof x === 'string') {
    return x + '- I am string'; // return number
  }

  if (typeof x === 'object') {
    return x; // return object;
  }

  return null; // for others return null
}

Now function test has inferenced output as union string | number | object | null. By using control flow and conditions, TS is able to understand what the function returns.

Every time your code deals with some polymorphic type, you can use type guards to specify exactly with what type you are working. The checking is done by the structure (because only structure exists in the runtime, type only annotates the structure during code writing), so you can check typeof, instanceof or if object has specific keys or values.

Very important thing to remember - Type is a label for some existing runtime structure. The label/type not exists during runtime, but structure does. That is why TS is able to understand type guards, as every type refers to some structure.

like image 138
Maciej Sikora Avatar answered Dec 28 '22 09:12

Maciej Sikora