Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] Typescript Overriding Type #50989

Closed
5 tasks done
mikoloism opened this issue Sep 29, 2022 · 8 comments
Closed
5 tasks done

[Proposal] Typescript Overriding Type #50989

mikoloism opened this issue Sep 29, 2022 · 8 comments
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript

Comments

@mikoloism
Copy link

mikoloism commented Sep 29, 2022

Typescript Overriding the Type

πŸ” Search Terms

  • typescript utility type override

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Sometimes we need to override the some property in same interface with another type; πŸ˜‰

πŸ“ƒ Motivating Example

playground

πŸ’» Use Cases

We can have this type alias as Type Tools :

type Override<
  T,
  K extends Partial<{ [P in keyof T]: any }> | string,
> = K extends string
  ? Omit<T, K> & { [P in keyof T]: T[P] | unknown }
  : Omit<T, keyof K> & K;

And, We have an interface alike below :

interface IUser {
  username: string;
  phone: string;
  // any else properties
}

Then, We need the same interface with some another type for username property

interface IEmployee extends IUser {
  username: [department: string, code: number]; // -> error
  tasks: Array<any>;
}

But If we use the Override type?

interface IEmployee extends Override<IUser, 'username'> {
  username: [department: string, code: number]; // -> work
  tasks: Array<any>;
}

Or This Syntax :

interface IEmplyee extends Override<IUser, { username: [department: string, code: number]; }> {
  tasks: Array<any>;
}

Or we have an ICustomer interface with same properties with IUser but for username property that should be given the number as the type

type ICustomer = Override<IUser, { username: number }>;

Also We can use alike below syntax :

interface ICustomer Override<IUser, { username: number }> {};

Work with Type Assignment :

// use-case
const employees: IEmployee = {
  username: ['any-string', 1234],
  tasks: [],
  phone: '09123456789'
};

// use-case
const customers: ICustomer = {
  username: 1234,
  phone: '09123456789'
};

Or any another example for this Overidde Utility Type, Thanks

Exactly this is just a Concept and with Week Example (take it easy πŸ˜‰)

@jcalz
Copy link
Contributor

jcalz commented Sep 29, 2022

This will be declined, since it's only for developer convenience, and not needed by the compiler to emit .d.ts files.

@fatcerberus
Copy link

You can already do extends Omit<T, 'prop'> so this type alias seems unnecessarily complicated (it has to do more than the bare minimum to even be useful as there's already a thing built-in that does its main job).

@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript Declined The issue was declined as something which matches the TypeScript vision labels Sep 29, 2022
@DanielRosenwasser
Copy link
Member

Omit is the preference here. Devs can replace the property with their preference of extending in an interface or intersecting with a new property.

@nopeless
Copy link

I would do something like this

interface User {
  username: string | string[],
  phone: string
}

type ShouldExtend<T, O> = T extends O ? T : never;
type Specify<T, O extends Record<string, any>> = ShouldExtend<Omit<T, keyof O> & O, T>;

function acceptUserWithStringName(u: Specify<User, {username: string}>): string {
	return u.username;
}

const u1 = {
	username: ['hello'],
	phone: '123'
}

const u2 = {
	username: 'miami',
	phone: '456'
};

acceptUserWithStringName(u1);
acceptUserWithStringName(u2);

demo

@mikoloism
Copy link
Author

mikoloism commented Sep 30, 2022

You can already do extends Omit<T, 'prop'> so this type alias seems unnecessarily complicated (it has to do more than the bare minimum to even be useful as there's already a thing built-in that does its main job).

Hi @fatcerberus πŸ‘‹
Yes Exactly, but Omit not for accept interface as generic type to overriding, omit just accept the key of interface but this Override Type give key and interface also πŸ˜‰

@mikoloism
Copy link
Author

mikoloism commented Sep 30, 2022

This will be declined, since it's only for developer convenience, and not needed by the compiler to emit .d.ts files.

Hi @jcalz πŸ‘‹

Exactly I'm Agree but:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand

@RyanCavanaugh RyanCavanaugh closed this as not planned Won't fix, can't repro, duplicate, stale Sep 30, 2022
@jedwards1211
Copy link

jedwards1211 commented Jul 30, 2024

@DanielRosenwasser

or intersecting with a new property

Intersecting with a new property doesn't behave the same way as interface/extends though... For example

// this type came from a library, I need to replace foo: any
type A = {
  foo: any
}

type B = A & { foo: number }

type T = B['foo'] // I need this to be number, but it's still any

interface/extends does get the desired behavior, but...if something is easier to do with interfaces, I want it to be just as easy with type literals. Type literals are just way more convenient because they can be used inline. For example, let's say I have

type Props = {
  b: A
}

And I want to constrain the type of b.foo to be number. Do I want to break out a separate interface declaration just to do that? No, that would be a hassle, having to think of a good name for a minor type adds to mental load. I would just:

type Props = {
  b: Omit<A, 'foo'> & { foo: number },
}

As such when there's more than one property to override, the Omit pattern becomes a pain because of the duplicated property names, and I find myself thinking I should declare an Override type that does what OP proposes. But then, I think, this should really just be a builtin.

@mikoloism
Copy link
Author

what if it's be a reusable keyword? πŸ€”

// basic
interface One {
	property: number;
}

// replace
interface Two extends One {
	override property: string ;
}

// union mode
interface Three extends One {
	extends property: string;
}

// cases
assertTypeEqual<One, { property: number }>;
assertTypeEqual<Two, { property: string }>;
assertTypeEqual<Three, { property: number | string }>;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

7 participants