1

I have a question regarding generic function (specifically why i'm able to modify an object from specific type)

example :

interface UserConfig {
  name: string;
  age: number;
}

let user: UserConfig = {
    name: "Eyal",
    age: 23,
 };

function addTimestamp<T>(arg: T): T {
  return { ...arg, timestamp: new Date().toString() };
}

console.log(user); // {name: 'Eyal',age: 23}

user = addTimestamp<UserConfig>(user);

console.log(user); // { name: 'Eyal', age: 23, timestamp: 2022-06-29T16:28:31.524Z }

Why i'm able to mutate user variable when it should have UserConfig interface properties only (name and age)

As this won't work

...
user.timestamp = "xxxx" // ERROR - Property 'timestamp' does not exist on type 'UserConfig'
1
  • 2
    Typescript is just a superset of Javascript. Once compiled all the types are gone. So if you relax the TS rules enough to go through the compilation process, you can add whatever you want to a object, even if it has a type in TS. This is because TS is compiled into normal JS. Commented Jun 29, 2022 at 17:02

1 Answer 1

1

First, you are not mutating "user", you are creating a new object that has all the fields of user, plus the timestamp field--the original user object doesn't change.

TypeScript doesn't complain here because the new object you return is still compatible with the original type T--you've not changed or removed any existing properties, you've just added new ones. It's structurally compatible with the original type.

interface UserConfig {
  name: string;
  age: number;
}

let user: UserConfig = {
  name: "Eyal",
  age: 23,
};

function addTimestamp<T>(arg: T): T {
  return { ...arg, timestamp: new Date().toString() };
}

let newUser: UserConfig = addTimestamp(user);

console.log(user === newUser); // false
console.log('timestamp' in user); // false
console.log('timestamp' in newUser); // true

let typeTest: UserConfig = (() => {
  return {
    name: 'Pizza',
    age: 49,
    isVeryCool: true
  }
})(); // compiles just fine

// @ts-expect-error
console.log(typeTest.isVeryCool); // doesn't compile if I remove the directive
console.log('isVeryCool' in typeTest); // true

You can argue that this is a weird thing to allow, especially given that TypeScript will block other kinds of related additions (for example, try removing the IIFE from my typeTest example and using the literal directly), but it's technically correct with the rest of the type system rules.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.