Related
Existing JavaScript code has "records" where the id is numeric and the other attributes string.
Trying to define this type:
type T = {
id: number;
[key:string]: string
}
gives error 2411 id type number not assignable to string
There is no specific type in TypeScript that corresponds to your desired structure. String index signatures must apply to every property, even the manually declared ones like id. What you're looking for is something like a "rest index signature" or a "default property type", and there is an open suggestion in GitHub asking for this: microsoft/TypeScript#17867. A while ago there was some work done that would have enabled this, but it was shelved (see this comment for more info). So it's not clear when or if this will happen.
You could widen the type of the index signature property so it includes the hardcoded properties via a union, like
type WidenedT = {
id: number;
[key: string]: string | number
}
but then you'd have to test every dynamic property before you could treat it as a string:
function processWidenedT(t: WidenedT) {
t.id.toFixed(); // okay
t.random.toUpperCase(); // error
if (typeof t.random === "string") t.random.toUpperCase(); // okay
}
The best way to proceed here would be if you could refactor your JavaScript so that it doesn't "mix" the string-valued bag of properties with a number-valued id. For example:
type RefactoredT = {
id: number;
props: { [k: string]: string };
}
Here id and props are completely separate and you don't have to do any complicated type logic to figure out whether your properties are number or string valued. But this would require a bunch of changes to your existing JavaScript and might not be feasible.
From here on out I'll assume you can't refactor your JavaScript. But notice how clean the above is compared to the messy stuff that's coming up:
One common workaround to the lack of rest index signatures is to use an intersection type to get around the constraint that index signatures must apply to every property:
type IntersectionT = {
id: number;
} & { [k: string]: string };
It sort of kind of works; when given a value of type IntersectionT, the compiler sees the id property as a number and any other property as a string:
function processT(t: IntersectionT) {
t.id.toFixed(); // okay
t.random.toUpperCase(); // okay
t.id = 1; // okay
t.random = "hello"; // okay
}
But it's not really type safe, since you are technically claiming that id is both a number (according to the first intersection member) and a string (according to the second intersection member). And so you unfortunately can't assign an object literal to that type without the compiler complaining:
t = { id: 1, random: "hello" }; // error!
// Property 'id' is incompatible with index signature.
You have to work around that further by doing something like Object.assign():
const propBag: { [k: string]: string } = { random: "" };
t = Object.assign({ id: 1 }, propBag);
But this is annoying, since most users will never think to synthesize an object in such a roundabout way.
A different approach is to use a generic type to represent your type instead of a specific type. Think of writing a type checker that takes as input a candidate type, and returns something compatible if and only if that candidate type matches your desired structure:
type VerifyT<T> = { id: number } & { [K in keyof T]: K extends "id" ? unknown : string };
This will require a generic helper function so you can infer the generic T type, like this:
const asT = <T extends VerifyT<T>>(t: T) => t;
Now the compiler will allow you to use object literals and it will check them the way you expect:
asT({ id: 1, random: "hello" }); // okay
asT({ id: "hello" }); // error! string is not number
asT({ id: 1, random: 2 }); // error! number is not string
asT({ id: 1, random: "", thing: "", thang: "" }); // okay
It's a little harder to read a value of this type with unknown keys, though. The id property is fine, but other properties will not be known to exist, and you'll get an error:
function processT2<T extends VerifyT<T>>(t: T) {
t.id.toFixed(); // okay
t.random.toUpperCase(); // error! random not known to be a property
}
Finally, you can use a hybrid approach that combines the best aspects of the intersection and generic types. Use the generic type to create values, and the intersection type to read them:
function processT3<T extends VerifyT<T>>(t: T): void;
function processT3(t: IntersectionT): void {
t.id.toFixed();
if ("random" in t)
t.random.toUpperCase(); // okay
}
processT3({ id: 1, random: "hello" });
The above is an overloaded function, where callers see the generic type, but the implementation sees the intersection type.
Playground link to code
You are getting this error since you have declared it as Indexable Type (ref: https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types) with string being the key type, so id being a number fails to conform to that declaration.
It is difficult to guess your intention here, but may be you wanted something like this:
class t {
id: number;
values = new Map<string, string>();
}
I had this same issue, but returned the id as a string.
export type Party = { [key: string]: string }
I preferred to have a flat type and parseInt(id) on the receiving code.
For my API, the simplest thing that could possibly work.
This question already has an answer here:
Function parameter type depends on another parameter
(1 answer)
Closed 6 months ago.
Can somebody please help me. How can I run this typescript code without issues at type checking?
type FuncsType = {
name1(argv1: string, argv2: string): void;
name2(argv1: number, argv2: number): void;
};
const functions: FuncsType = {
name1: (argv1, argv2) => {
console.log(argv1, argv2);
},
name2: (argv1, argv2) => {
console.log(argv1, argv2);
}
};
type start = (name: keyof FuncsType, ...args: Parameters<FuncsType[keyof FuncsType]>) => void;
const start: start = (name, ...args) => {
const func = functions[name] as FuncsType[keyof FuncsType];
func(...args);
};
start("name1", "1", "2");
start("name2", 3, 2);
error
I found out that the problem no longer occurs when I specify the rest arguments of a certain function.
success hack
So, in principle you could just do the following and get the same result with the benefit of being less verbose and clearer about your intentions.
function start(args: number[] | string[]): void {
console.log(args);
}
start(['1', '2']);
start([3, 2]);
I personally prefer to provide an array as an argument and be explicit about what it is I am providing, instead of saying: "Here. Take the remaining arguments", which is what you are doing if you're using the spread operator.
Now let's say you want to do something more than just console.log() something and you actually want to call a separate function based on a condition. I understand your desire to put this in an object, but I'd personally just make check for the contents of your args like this:
function start(args: number[] | string[]): void {
args.forEach(arg => {
if (arg === typeof 'string') {
// do something
}
if (arg === typeof 'number') {
// do something else
}
});
}
I'm personally not a fan of using objects to look up functions...
In my opinion you also don't need to type your functions. So I wouldn't worry too much about doing things like this:
const start: start = () => {}
I would however advise on communicating a functions return type, so in this case omitting the function type and showing the return type:
const start = (): void => {}
TypeScript is about readability for me, so I try to be clear and communicate with others (and my future self) by using types, but I try to avoid complex generics and unions until I really need them.
Now, a quick word about the spread operator (...) for arrays in JavaScript:
const array = [1,2,3,4,5]
console.log(array) // [1,2,3,4,5]
is exactly the same as saying:
const array = [1,2,3,4,5]
console.log([...array]) // [1,2,3,4,5]
Note: If you don't provide the square brackets, you will actually just get the numbers and NOT an array.
const array = [1,2,3,4,5]
console.log(...array) // 1 2 3 4 5
On a sidenote Optional Parameters or types can be marked with a '?'
as in:
class SomeClass
myVariable1?: string; // can be omitted when calling the constructor new
myVariable2: Number; // SomeClass(1) or not new SomeClass("somestring", 2);
constructor(omittableConstructorParameter1?:string,
notOmittableConstructorParameter2:Number) {
this.myVariable1 = ommitableConstructorArgument;
this.myVariable2 = notOmmitableConstructorArgument;
}
this concept applies to Interfaces, too (ofc without the constructor)
Is there a way to create type in typescript with methods that when I call these methods, they have access to the variable's value? Equals what the array, for example, which has the method find.
Example:
const arrayVar: Array = [1,2,3];
array.find(el => el === 1);
In this case, find has access to the value of the array arrayVar without me having to pass it via a parameter to a function, for example, I wanted to create something in this way, for example:
const myVar: MyCustomType = 5;
myVar.add(2); // Nesse caso, o retorno seria 7.
I know it can be done with classes and functions, but then I would have to pass the value of "myVar" as a parameter (function add (value1, value2), for example), I wanted a way to access it directly, just like the type Array does in its methods.
To make a subclass of Number with new methods:
class SwagNumber extends Number {
add(number: number) {
// Tell the TS compiler that `this` is an unboxed Number
return (this as unknown as number) + number;
}
}
Then to use:
const six = new SwagNumber(6);
six will be typed to SwagNumber by the TS compiler.
And to show it works:
six.add(5)
> 11
Let's look at the Constructor used, part of the Class:
> six.constructor
[class SwagNumber extends Number]
This will also leave the original Number prototype unchanged, which will stop any potential issues (double dots are used to use a method on a number, to distinguish the dot from a decimal point!)
> 3..constructor
[Function: Number]
or:
> (3).constructor
[Function: Number]
See Classes on MDN
However there's some danger here
Since SwagNumber is an object, and a regular number isn't an object by default (until you call its methods), comparisons won't work properly:
> six === 6
false
See Why should you not use Number as a constructor?
You could do this by adding your add method to a prototype:
interface Number {
add: (val:number) => number;
}
Number.prototype.add = function(val:number): number{
return (this as number) + val;
}
var myVal: Number = 5
console.log(myVal.add(7))
TS Playground Link
If you want to invoke functions on an object at this point create a dedicated class. Why? Because you have structured zone where you can add/remove/edit and reuse in other part of code.
Sorry, i write in JS but you can change with no effort in TypeScript
Create Class Element
class CustomArray {
constructor() {
this.arr = [];
}
add(value) {
this.arr.push(value)
}
erase(){
this.arr = [];
}
// so on...
print(){
console.log(this.arr)
}
}
//I use Module, so i will use require.
module.exports = CustomArray;
The above class is simplest example. You can decorate with other functions. In theory as many as you want
FOR USE INSIDE OTHER CONTEXT
const CustomArray = require("./CustomArray");
var arr = new CustomArray();
arr.add(2)
arr.print()
I'm trying to find a way to pass an object to function in and check it type in a runtime. This is a pseudo code:
function func (obj:any) {
if(typeof obj === "A") {
// do something
} else if(typeof obj === "B") {
//do something else
}
}
let a:A;
let b:B;
func(a);
But typeof always returns "object" and I could not find a way to get the real type of a or b. instanceof did not work either and returned the same.
Any idea how to do it in TypeScript?
Edit: I want to point out to people coming here from searches that this question is specifically dealing with non-class types, ie object
shapes as defined by interface or type alias. For class types you
can use JavaScript's instanceof to determine the class an instance comes from, and TypeScript will narrow the type in the type-checker automatically.
Types are stripped away at compile-time and do not exist at runtime, so you can't check the type at runtime.
What you can do is check that the shape of an object is what you expect, and TypeScript can assert the type at compile time using a user-defined type guard that returns true (annotated return type is a "type predicate" of the form arg is T) if the shape matches your expectation:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return obj.foo !== undefined
}
function isB(obj: any): obj is B {
return obj.bar !== undefined
}
function func(obj: any) {
if (isA(obj)) {
// In this block 'obj' is narrowed to type 'A'
obj.foo;
}
else if (isB(obj)) {
// In this block 'obj' is narrowed to type 'B'
obj.bar;
}
}
Example in Playground
How deep you take the type-guard implementation is really up to you, it only needs to return true or false. For example, as Carl points out in his answer, the above example only checks that expected properties are defined (following the example in the docs), not that they are assigned the expected type. This can get tricky with nullable types and nested objects, it's up to you to determine how detailed to make the shape check.
Expanding on Aaron's answer, I've made a transformer that generates the type guard functions at compile time. This way you don't have to manually write them.
For example:
import { is } from 'typescript-is';
interface A {
foo: string;
}
interface B {
bar: number;
}
if (is<A>(obj)) {
// obj is narrowed to type A
}
if (is<B>(obj)) {
// obj is narrowed to type B
}
You can find the project here, with instructions to use it:
https://github.com/woutervh-/typescript-is
"I'm trying to find a way to pass an object to function in and check it type in a runtime".
Since a class instance is just an object, the "native" answer is to use a class instance and instanceof when runtime type checking is needed, use an interface when not in order to keep a contract and decouple your application, make reduce signature size on methods/ctors, while not add any additional size. In my humble opinion this is one of a few main considerations for me in TypeScript when deciding to use a class vs type/interface. One other main driving factor is whether the object will ever need to be instantiated vs if it for instance defines a POJO.
In my codebase, I will typically have a class which implements an interface and the interface is used during compilation for pre-compile time type safety, while classes are used to organize my code and allow for ease in passing data between functions, classes and method as well as do runtime type checks in typescript.
Works because routerEvent is an instance of NavigationStart class
if (routerEvent instanceof NavigationStart) {
this.loading = true;
}
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationCancel ||
routerEvent instanceof NavigationError) {
this.loading = false;
}
Will not work
// Must use a class not an interface
export interface IRouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
Will not work
// Must use a class not a type
export type RouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
As you can see by the code above, classes are used to compare the instance to the types NavigationStart|Cancel|Error within the Angular library and if you have used the router before you a project I am willing to be that you have done similar if not identical checks within your own codebase in order to determine application state during runtime.
Using instanceof on a Type or Interface is not possible, since the ts compiler strips away these attributes during its compilation process and prior to being interpreted by JIT or AOT. Classes are a great way to create a type which can be used pre-compilation as well as during the JS runtime.
Update 2022
In addition to my original response to this, you can leverage The TypeScript Reflect Metadata API or roll your own solution using the TypeScript compiler to do static analysis of your code and parse the AST, querying like so:
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
// ...
break;
case ts.SyntaxKind.TypeDeclaration:
// ...
break;
}
See this solution for additonal details
I've been playing around with the answer from Aaron and think it would be better to test for typeof instead of just undefined, like this:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return typeof obj.foo === 'string'
}
function isB(obj: any): obj is B {
return typeof obj.bar === 'number'
}
function func(obj: any) {
if (isA(obj)) {
console.log("A.foo:", obj.foo);
}
else if (isB(obj)) {
console.log("B.bar:", obj.bar);
}
else {console.log("neither A nor B")}
}
const a: A = { foo: 567 }; // notice i am giving it a number, not a string
const b: B = { bar: 123 };
func(a); // neither A nor B
func(b); // B.bar: 123
No, You cannot reference a type in runtime, but yes you can convert an object to a type with typeof, and do validation/sanitisation/checks against this object in runtime.
const plainObject = {
someKey: "string",
someKey2: 1,
};
type TypeWithAllOptionalFields = Partial<typeof plainObject>; //do further utility typings as you please, Partial being one of examples.
function customChecks(userInput: any) {
// do whatever you want with the 'plainObject'
}
Above is equal as
type TypeWithAllOptionalFields = {
someKey?: string;
someKey2?: number;
};
const plainObject = {
someKey: "string",
someKey2: 1,
};
function customChecks(userInput: any) {
// ...
}
but without duplication of keynames in your code
You can call the constructor and get its name
let className = this.constructor.name
I know this is an old question and the "true" question here is not the
same as the question in the title, but Google throws this question for
"typescript runtime types" and some people may know what they are
looking for and it can be runtime types.
Right answer here is what Aaron Beall answered, the type guards.
But answer, matching question in the title and matching Google
searches, is only usage of TypeScript transformers/plugins. TypeScript
itself strip out information about types when transpiling TS to JS.
And well,.. it is one of the possible ways how to implement the type guards,
eg. the typescript-is transformer as user7132587 pointed out.
Another options is transformer tst-reflect. It provides all the information about types at runtime. It allows you to write own type guards based on type information, eg. checking that object has all the properties you expect. Or you can use directly Type.is(Type) method from the transformer, which is based directly on the TypeScript's type checking information.
I've created this REPL. Have a fun!
More info in Github repository.
import { getType, Type } from "tst-reflect";
class A {
alphaProperty: string;
}
interface B {
betaProperty: string;
}
class Bb extends A implements B {
betaProperty = "tst-reflect!!";
bBetaProperty: "yes" | "no" = "yes";
}
/** #reflectGeneric */
function func<TType>(obj?: TType)
{
const type: Type = getType<TType>();
console.log(
type.name,
"\n\textends", type.baseType.name,
"\n\timplements", type.getInterface()?.name ?? "nothing",
"\n\tproperties:", type.getProperties().map(p => p.name + ": " + p.type.name),
"\n"
);
console.log("\tis A:", type.is(getType<A>()) ? "yes" : "no");
console.log("\tis assignable to A:", type.isAssignableTo(getType<A>()) ? "yes" : "no");
console.log("\tis assignable to B:", type.isAssignableTo(getType<B>()) ? "yes" : "no");
}
let a: A = new A();
let b: B = new Bb();
let c = new Bb();
func(a);
func<typeof b>();
func<Bb>();
Output:
A
extends Object
implements nothing
properties: [ 'alphaProperty: string' ]
is A: yes
is assignable to A: yes
is assignable to B: no
B
extends Object
implements nothing
properties: [ 'betaProperty: string' ]
is A: no
is assignable to A: no
is assignable to B: yes
Bb
extends A
implements B
properties: [ 'betaProperty: string', 'bBetaProperty: ' ]
is A: no
is assignable to A: yes
is assignable to B: yes
You should use a separate dynamic typing library that allows you to define custom types with dynamic type information and track it's compliance to the expected type.
The best solution that allows you this is this amazing library: https://github.com/pelotom/runtypes
Using it, you can define a meta-type for your A and B types:
const AType = Record({foo: Number})
const BType = Record({baz: String})
This is pure TS, and you can note that we are creating constant objects, not static types. Also, we are using Number and String objects provided by the library, and not the TS static types of number and string.
Then, you create the static type declarations for A and B:
type A = Static<typeof AType>
type B = Static<typeof BType>
Now, these types are proper Typescript static types. They contain all the proper members that you passed during the creation of the meta-type, up to the infinite depth of objects. Arrays, objects, optionals, faulsy values, scalar types are all supported.
Then you can use this like this:
function asdf(object: any): A | undefined {
try {
const aObject = AType.check(object) // returns A if complies with Record definition, throws otherwise
return aObject
} catch {
return undefined
}
}
asdf({ foo: 3 }) // returns A, literally the same object since it passed the check
asdf({ bar: "3" }) // returns undefined, because no `foo` of type `number`
asdf({ foo: "3" }) // returns undefined, because `foo` has wrong type
This is the most modern, serious solution that works and scales beautifully.
You should use the "in" operator to narrow down. Reference
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
Alternative approach without the need of checking the type
What if you want to introduce more types? Would you then extend your if-statement? How many such if-statements do you have in your codebase?
Using types in conditions makes your code difficult to maintain. There's lots of theory behind that, but I'll save you the hazzle. Here's what you could do instead:
Use polymorphism
Like this:
abstract class BaseClass {
abstract theLogic();
}
class A extends BaseClass {
theLogic() {
// do something if class is A
}
}
class B extends BaseClass {
theLogic() {
// do something if class is B
}
}
Then you just have to invoke theLogic() from whichever class you want:
let a: A = new A();
a.theLogic();
let b: B = new B();
b.theLogic();
If I wanted to programatically assign a property to an object in Javascript, I would do it like this:
var obj = {};
obj.prop = "value";
But in TypeScript, this generates an error:
The property 'prop' does not exist on value of type '{}'
How am I supposed to assign any new property to an object in TypeScript?
Index types
It is possible to denote obj as any, but that defeats the whole purpose of using typescript. obj = {} implies obj is an Object. Marking it as any makes no sense. To accomplish the desired consistency an interface could be defined as follows.
interface LooseObject {
[key: string]: any
}
var obj: LooseObject = {};
OR to make it compact:
var obj: {[k: string]: any} = {};
LooseObject can accept fields with any string as key and any type as value.
obj.prop = "value";
obj.prop2 = 88;
The real elegance of this solution is that you can include typesafe fields in the interface.
interface MyType {
typesafeProp1?: number,
requiredProp1: string,
[key: string]: any
}
var obj: MyType ;
obj = { requiredProp1: "foo"}; // valid
obj = {} // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number
obj.prop = "value";
obj.prop2 = 88;
Record<Keys,Type> utility type
Update (August 2020): #transang brought this up in comments
Record<Keys,Type> is a Utility type in typescript. It is a much cleaner alternative for key-value pairs where property-names are not known.
It's worth noting that Record<Keys,Type> is a named alias to {[k: Keys]: Type} where Keys and Type are generics.
IMO, this makes it worth mentioning here
For comparison,
var obj: {[k: string]: any} = {};
becomes
var obj: Record<string,any> = {}
MyType can now be defined by extending Record type
interface MyType extends Record<string,any> {
typesafeProp1?: number,
requiredProp1: string,
}
While this answers the Original question, the answer here by #GreeneCreations might give another perspective on how to approach the problem.
Or all in one go:
var obj:any = {}
obj.prop = 5;
This solution is useful when your object has Specific Type. Like when obtaining the object to other source.
let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
I tend to put any on the other side i.e. var foo:IFoo = <any>{}; So something like this is still typesafe:
interface IFoo{
bar:string;
baz:string;
boo:string;
}
// How I tend to intialize
var foo:IFoo = <any>{};
foo.bar = "asdf";
foo.baz = "boo";
foo.boo = "boo";
// the following is an error,
// so you haven't lost type safety
foo.bar = 123;
Alternatively you can mark these properties as optional:
interface IFoo{
bar?:string;
baz?:string;
boo?:string;
}
// Now your simple initialization works
var foo:IFoo = {};
Try it online
Although the compiler complains it should still output it as you require. However, this will work.
const s = {};
s['prop'] = true;
I'm surprised that none of the answers reference Object.assign since that's the technique I use whenever I think about "composition" in JavaScript.
And it works as expected in TypeScript:
interface IExisting {
userName: string
}
interface INewStuff {
email: string
}
const existingObject: IExisting = {
userName: "jsmith"
}
const objectWithAllProps: IExisting & INewStuff = Object.assign({}, existingObject, {
email: "jsmith#someplace.com"
})
console.log(objectWithAllProps.email); // jsmith#someplace.com
Advantages
type safety throughout because you don't need to use the any type at all
uses TypeScript's aggregate type (as denoted by the & when declaring the type of objectWithAllProps), which clearly communicates that we're composing a new type on-the-fly (i.e. dynamically)
Things to be aware of
Object.assign has it's own unique aspects (that are well known to most experienced JS devs) that should be considered when writing TypeScript.
It can be used in a mutable fashion, or an immutable manner (I demonstrate the immutable way above, which means that existingObject stays untouched and therefore doesn't have an email property. For most functional-style programmers, that's a good thing since the result is the only new change).
Object.assign works the best when you have flatter objects. If you are combining two nested objects that contain nullable properties, you can end up overwriting truthy values with undefined. If you watch out for the order of the Object.assign arguments, you should be fine.
One more option do to that is to access the property as a collection:
var obj = {};
obj['prop'] = "value";
You can create new object based on the old object using the spread operator
interface MyObject {
prop1: string;
}
const myObj: MyObject = {
prop1: 'foo',
}
const newObj = {
...myObj,
prop2: 'bar',
}
console.log(newObj.prop2); // 'bar'
TypeScript will infer all the fields of the original object and VSCode will do autocompletion, etc.
you can use this :
this.model = Object.assign(this.model, { newProp: 0 });
Case 1:
var car = {type: "BMW", model: "i8", color: "white"};
car['owner'] = "ibrahim"; // You can add a property:
Case 2:
var car:any = {type: "BMW", model: "i8", color: "white"};
car.owner = "ibrahim"; // You can set a property: use any type
Late but, simple answer
let prop = 'name';
let value = 'sampath';
this.obj = {
...this.obj,
[prop]: value
};
Simplest will be following
const obj = <any>{};
obj.prop1 = "value";
obj.prop2 = "another value"
Here is a special version of Object.assign, that automatically adjusts the variable type with every property change. No need for additional variables, type assertions, explicit types or object copies:
function assign<T, U>(target: T, source: U): asserts target is T & U {
Object.assign(target, source)
}
const obj = {};
assign(obj, { prop1: "foo" })
// const obj now has type { prop1: string; }
obj.prop1 // string
assign(obj, { prop2: 42 })
// const obj now has type { prop1: string; prop2: number; }
obj.prop2 // number
// const obj: { prop1: "foo", prop2: 42 }
Note: The sample makes use of TS 3.7 assertion functions. The return type of assign is void, unlike Object.assign.
Since you cannot do this:
obj.prop = 'value';
If your TS compiler and your linter does not strict you, you can write this:
obj['prop'] = 'value';
If your TS compiler or linter is strict, another answer would be to typecast:
var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";
To guarantee that the type is an Object (i.e. key-value pairs), use:
const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'
It is possible to add a member to an existing object by
widening the type (read: extend/specialize the interface)
cast the original object to the extended type
add the member to the object
interface IEnhancedPromise<T> extends Promise<T> {
sayHello(): void;
}
const p = Promise.resolve("Peter");
const enhancedPromise = p as IEnhancedPromise<string>;
enhancedPromise.sayHello = () => enhancedPromise.then(value => console.info("Hello " + value));
// eventually prints "Hello Peter"
enhancedPromise.sayHello();
The best practice is use safe typing, I recommend you:
interface customObject extends MyObject {
newProp: string;
newProp2: number;
}
Extending #jmvtrinidad solution for Angular,
When working with a already existing typed object, this is how to add new property.
let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
Now if you want to use otherProperty in html side, this is what you'd need:
<div *ngIf="$any(user).otherProperty">
...
...
</div>
Angular compiler treats $any() as a cast to the any type just like in TypeScript when a <any> or as any cast is used.
Store any new property on any kind of object by typecasting it to 'any':
var extend = <any>myObject;
extend.NewProperty = anotherObject;
Later on you can retrieve it by casting your extended object back to 'any':
var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;
To preserve your previous type, temporary cast your object to any
var obj = {}
(<any>obj).prop = 5;
The new dynamic property will only be available when you use the cast:
var a = obj.prop; ==> Will generate a compiler error
var b = (<any>obj).prop; ==> Will assign 5 to b with no error;
dynamically assign properties to an object in TypeScript.
to do that You just need to use typescript interfaces like so:
interface IValue {
prop1: string;
prop2: string;
}
interface IType {
[code: string]: IValue;
}
you can use it like that
var obj: IType = {};
obj['code1'] = {
prop1: 'prop 1 value',
prop2: 'prop 2 value'
};
The only solution that is fully type-safe is this one, but is a little wordy and forces you to create multiple objects.
If you must create an empty object first, then pick one of these two solutions. Keep in mind that every time you use as, you're losing safety.
Safer solution
The type of object is safe inside getObject, which means object.a will be of type string | undefined
interface Example {
a: string;
b: number;
}
function getObject() {
const object: Partial<Example> = {};
object.a = 'one';
object.b = 1;
return object as Example;
}
Short solution
The type of object is not safe inside getObject, which means object.a will be of type string even before its assignment.
interface Example {
a: string;
b: number;
}
function getObject() {
const object = {} as Example;
object.a = 'one';
object.b = 1;
return object;
}
Simply do this, and you can add or use any property. (I am using typescript version as "typescript": "~4.5.5")
let contextItem = {} as any;
Now, you can add any property and use it any where. like
contextItem.studentName = "kushal";
later you can use it as:
console.log(contextItem.studentName);
I wrote an article tackling this very topic:
Typescript – Enhance an object and its type at runtime
https://tech.xriba.io/2022/03/24/typescript-enhance-an-object-and-its-type-at-runtime/
Maybe you could take inspiration from Typescript concepts such:
Mapped Types
Key Remapping via as
Intersection Types
If you are using Typescript, presumably you want to use the type safety; in which case naked Object and 'any' are counterindicated.
Better to not use Object or {}, but some named type; or you might be using an API with specific types, which you need extend with your own fields. I've found this to work:
class Given { ... } // API specified fields; or maybe it's just Object {}
interface PropAble extends Given {
props?: string; // you can cast any Given to this and set .props
// '?' indicates that the field is optional
}
let g:Given = getTheGivenObject();
(g as PropAble).props = "value for my new field";
// to avoid constantly casting:
let k = getTheGivenObject() as PropAble;
k.props = "value for props";
You can add this declaration to silence the warnings.
declare var obj: any;
I ran into this problem when trying to do a partial update of an object that was acting as a storage for state.
type State = {
foo: string;
bar: string;
baz: string;
};
const newState = { foo: 'abc' };
if (someCondition) {
newState.bar = 'xyz'
}
setState(newState);
In this scenario, the best solution would be to use Partial<T>. It makes all properties on the provided type optional using the ? token. Read more about it in a more specific SO topic about making all properties on a type optional.
Here's how I solved it with Partial<T>:
type State = {
foo: string;
bar: string;
baz: string;
};
const newState: Partial<State> = { foo: 'abc' };
if (someCondition) {
newState.bar = 'xyz';
}
setState(newState);
This is similar to what fregante described in their answer, but I wanted to paint a clearer picture for this specific use case (which is common in frontend applications).
Use ES6 Map whenever a map can take truly arbitrary values of fixed type, and optional properties otherwise
I think this is the guideline I'll go for. ES6 map can be done in typescript as mentioned at: ES6 Map in Typescript
The main use case for optional properties are "options" parameters of functions: Using named parameters JavaScript (based on typescript) In that case, we do know in advance the exact list of allowed properties, so the sanest thing to do is to just define an explicit interface, and just make anything that is optional optional with ? as mentioned at: https://stackoverflow.com/a/18444150/895245 to get as much type checking as possible:
const assert = require('assert')
interface myfuncOpts {
myInt: number,
myString?: string,
}
function myfunc({
myInt,
myString,
}: myfuncOpts) {
return `${myInt} ${myString}`
}
const opts: myfuncOpts = { myInt: 1 }
if (process.argv.length > 2) {
opts.myString = 'abc'
}
assert.strictEqual(
myfunc(opts),
'1 abc'
)
And then I'll use Map when it is something that is truly arbitrary (infinitely many possible keys) and of fixed type, e.g.:
const assert = require('assert')
const integerNames = new Map<number, string>([[1, 'one']])
integerNames.set(2, 'two')
assert.strictEqual(integerNames.get(1), 'one')
assert.strictEqual(integerNames.get(2), 'two')
Tested on:
"dependencies": {
"#types/node": "^16.11.13",
"typescript": "^4.5.4"
}
Try this:
export interface QueryParams {
page?: number,
limit?: number,
name?: string,
sort?: string,
direction?: string
}
Then use it
const query = {
name: 'abc'
}
query.page = 1