1
votes

Let's analyse code snippet below:

class Human {
    private name: string;
    constructor(name){
        this.name = name;
    }
}

let h = new Human(5)

Above code doesn't throw any error. And I'd expect it to throw in the constructor call, where I pass 5.

It seems that constructor's name parameter gets inferred as any, where, statically it's easy to find out that I'm assigning it to a private name: string.

The question is: is there any particular reason that TypeScript is allowing 5 here - or in other words - that name is inferred as any, when in this case it's obvious that it has to be a string?

I know I could do another class definition like this:

class Human {
    constructor(
        private name: string
    ){}
}

but here I specify the parameter type, so there's no inference here. The same way I could do:

class Human {
    private name: string;
    constructor(name: string){
        this.name = name;
    }
}

and there would be no inference either. My question is about inference - why it works this way.

3
I think it's because typescript is compatible to and a subset of javascript. default type any made this way. - Val

3 Answers

3
votes

There is a specific compiler option noImplicitAny specifically to address this. Without "noImplicitAny": true, your name is inferred as any so it does not complain when a number is passed.

Why it works this way?

See this and this.

2
votes

contructor( name ) declaration is equal to constructor( name: any ). Then, this.name = name assignment works seamlessly only because TS is backward-compatible with plain JS: when you assign the 'any' value to a strongly typed var, TS assumes you know what you're doing. So, conversion between number and string stays out of TS control and responsibility.

0
votes

I think it's a matter of ambiguity and computational complexity.

First of all, simple rules covering most of the cases are the best. This means the grammar of the language is simple, it's easy to learn it for newcomers and also easy to maintain the compiler itself.

Then, there could be corner cases where inferring a type of an argument by its usage would require a lot of traversing and thus a typo in the code could slow down the compilation.

Also, constructor is a type's external interface which is used by other parts of the application, so it should be explicit for clarity and safety. If your interface implicitly depends on the actual implementation, you can accidentally change something during refactoring and get hard-to-detect bugs.