41

Thanks for your patience here, I'm just starting out with TypeScript.

I'm working on an angular 2 app that needs to accept text inputs and then make a bunch of calculations. I (incorrectly?) assumed that i would need to first bind inputs to "any" type variables within my data model and then convert those variables to numbers in order to crunch numbers. I've looked all around, and can't find how to do this in such a way that it doesn't throw this TS compiler error:

`src/calculator_service.ts(40,5): error TS2322: Type 'number' is not assignable to type 'string'.`

Within my CalculatorService, I have this function:

/*
 * Convert the object of strings recieved from the form into a clean object of integers
 */
n(model:ModelFields) {
    // Clone it
    this.numericModel = Object.assign({}, this.model);

    for (var prop in this.numericModel) {
        if (this.numericModel.hasOwnProperty(prop)) {

            // strip off all non-numeric charactersklj
            this.numericModel[prop] = this.numericModel[prop].replace(/\D/g,'');

            // convert to Any typescript type
            // this breaks the application, and still throws a compiler error. nope.
            // this.numericModel[prop] = this.numericModel[prop]:Any;

            // convert to Number type
            // this gives a typescript console error, but seems to still compile... 
            // ignoring this for now in order to meet deadline
            this.numericModel[prop] = +this.numericModel[prop];

        }
    }

    return this.numericModel;
}

and the ModelFields definition (thanks tarh!)

export class ModelFields { 
    constructor( 
        public fieldName: any, 
        public anotherField: any 
    ) 
    {} 
}

Any ideas? Thanks everybody!

3
  • 1
    What is the definition of ModelFields? Commented Nov 9, 2015 at 18:42
  • 1
    @Tarh export class ModelFields { constructor( public fieldName: any, public anotherField: any ) {} } Commented Nov 10, 2015 at 12:27
  • @ryanrain it's been a long time but I'm curious at this point, did my post answer your question? Commented Feb 1, 2019 at 8:18

6 Answers 6

40

You cannot change a variable's type in TypeScript, that's just the opposite TS was made for. Instead, you can declare a variable as "any", which would be equivalent to a classic "var" variable in JS, untyped.

Once a variable is declared, you will not be able to retype it. What you could do, however, is to declare "any" and then cast it whenever you want to use it, in order to use it as the desired type.

For example this would not throw any errors:

let a: any;

a = 1234;
(a as number).toExponential();

a = "abcd"; 
(a as string).substr(1, 4);

In case of your class, this would be also correct, no type errors:

class ModelFields { 
    constructor( 
        public fieldName: any, 
        public anotherField: any 
    ) 

    //...
}

let model: ModelFields = new ModelFields(1, 2);

console.log(model.fieldName + model.anotherField);    // --> 3

model.fieldName = "a";
model.anotherField = "b";

console.log(model.fieldName + model.anotherField);    // --> ab
Sign up to request clarification or add additional context in comments.

2 Comments

In this scenario you should use "unknown" instead of "any". You should avoid using "any" unless absolutely necessary.
Strongly disagree with the first sentence. For instance, if I have an entry: FileSystemEntry and do if (entry.isFile) { } then inside the if I should be able to say that entry now has type FileSystemFileEntry. There is runtime-only narrowing that should be permissible when it's from a parent interface into an extending interface.
4

You example is not clear enough, but I guess that your problem is because of Typescript inference:

var x = 3; // x is a number
x = "45";  // compiler error

But, if you do:

var x : any = 3; // x can be anything
x = "45";

Or:

var x; // x is any forever
x = '45';  // x is still any

You can find further details on those great slides and on docs

Hope this can help a bit...

3 Comments

Thanks a lot Yaniv, and sorry for my lack of clarity. I think I understand the concept of TypeScript inference - very important. In my code it's probably my use of the replace string method that makes TypeScript infoer that my model is an object of strings.
To clarify, I'm trying to ask if it's possible to explicitly and correctly change a variable's type, say from a string to a number, without causing a compiler error. Does this make sense?
URL broken for slides.
2

You can create a new type by omitting the properties from the old type, then adding them back.

This will not work.

interface DbItem {
 _id: Buffer
 date: number
}

interface JsItem extends DbItem {
 _id: string
}

But you can used the utility type Omit to omit the types you want to change, and then add them back.

interface DbItem {
 _id: Buffer
 date: number
}

interface JsItem extends Omit<DbItem, '_id'> {
 _id: string
}

Comments

1

easiest way is

let b = (a as object) as type; // here type may be any data-type

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

Faced a similar type of query and worked for me.

My case:

article Id comes in the string format from route params and from API I get the data in number format.

If I check with !=, ES lint throws an error. So I am converting a string to number in vanilla javascript using Number() method.

const articleId = Number(this.route.snapshot.params['articleId']);

data.forEach((element, index) => {

    // console.log(typeof(element['id']), element['id']); 
    // 4, number
    // console.log(typeof(this.route.snapshot.params['articleId']), this.route.snapshot.params['articleId']);
    // 4, string (converted to number)

    if (element['id'] !== articleId) {
        //my implementation
     }
}

Reference Link:

  1. https://gomakethings.com/converting-strings-to-numbers-with-vanilla-javascript/

2 Comments

Just add a plus sign: +this.route.snapshot.params['articleId']
@0zkrPM Watch out for empty strings, +'' evaluates to 0 (zero), which is the equivalent of Number(''). I prefer using POST rather than GET so that the argument is already a number e.g. {articleId: 123} (our APIs aren't public).
0

Either re-declare the variable:

// Re-declare the variable

let value: any = Date.now() % 2 === 0 ? "string" : 124875;
if (typeof value === 'string') {
  let str: string = value as string;
} else {
  let num: number = value as number;
}

or map to another class instance:

export class ModelField {
  constructor(public fieldName: any) {}
}

export class StringField { 
    public x: string; 
    constructor(model: ModelField) {
      this.x = model.fieldName as string;
    } 
}

let value: any = Date.now() % 2 === 0 ? "string" : 124875;
let model: ModelField = new ModelField(value);
if (typeof model.fieldName === 'string') {
  let strField: StringField = new StringField(model);
} else {
  let numField: NumberField = new NumberField(model);
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.