I have many models ( which are javascript classes ) holding names for my database, and also it's types. Is there a way I can create a new User without having to call all those attributes inside the constructor ?
class User {
name: string;
email: string;
password: string;
constructor(values: User) {
this.name = values.name;
this.email = values.email;
this.password = values.password;
// I don't want to call this.name, this.email, ... Since this User model is relatively small, but I have models with hundreds of attributes
}
}
In typescript you can create class fields in the constructor's argument list, like this:
class User {
// nothing here :)
constructor(private name: string, private email: string, private password: string) {
// empty :)
}
}
If you specify an access modifier, you get the declaration and the assignment automatically.
Related
Getting error after assigning an interface to any object in typescript.
I have two below interface.
export interface UserInfo {
firstName: string,
lastName: string,
lastUpdateTime: string,
status: string
}
export interface User{
activeCount: string,
subsource: string,
users: UserInfo[]
}
After assign the above User interface to an object in a component getting error.
user: User= {}; // Type {} is missing the properties
user: User;
You can make properties optional by using the Partial utility type.
const user: Partial<User> = {};
Or do the same thing manually by mapping the properties.
interface UserInfo {
firstName: string;
lastName: string;
lastUpdateTime: string;
status: string;
}
interface User {
activeCount: string;
subsource: string;
users: UserInfo[];
}
export type MakeOptional<T> = { [K in keyof T]?: T[K] };
const user: MakeOptional<User> = {};
This would be a more generic solution for your problem.
Its because when you specify a property of the type like this firstName: string the property should be present (its required) please change its definition to firstName?: string, which means the property is an optional property and can be not defined!
Then the error will go away!
export interface UserInfo {
firstName?: string,
lastName?: string,
lastUpdateTime?: string,
status?: string
}
export interface User{
activeCount?: string,
subsource?: string,
users?: UserInfo[]
}
After assign the above User interface to an object in a component getting error.
user: User= {}; // Type {} is missing the properties - wont come now!
user: User;
Of course, you need to add the properties to the object:
const user: User = {
users: [],
activeCount: "",
subsource: "",
};
I think you have to tell the compiler that {} is a type of User and contains the same properties as the User interface. For example:
user:User = {} as User;
as per typescript,
You Define user: User it set value of user as User
to use interface you have to define like : let user: User
I'm new to typescript and am writing some classes to work as a very simple ORM for my project. I just learned how to require that a parameter conform to an interface, like so:
interface UserInfo {
userId: string
email?: string
// many more properties...
}
class User {
constructor(connection: typeof db, init: UserInfo) {
// code here
And I understand that the syntax to set default values for those optional properties is:
class User {
constructor(connection: typeof db, {email = null, /*...*/}: UserInfo) {
// code here
But my UserInfo interface is long, what if I want to only specify/default a few of the properties, without typing out a giant object in the parameters for the constructor of this object? Is there a way to require an entire interface, but destructure only some properties?
Destructuring isn't really a great fit, because that essentially ignores any properties you don't explicitly mention. In order not to lose track of the unmentioned properties, you could destructure into a rest object:
class User {
constructor({ email = "xyz", ...rest }: UserInfo) {}
}
But now you have a variable named email with a string value in it which is either the one passed in or the default "xyz" (not null, by the way; null is not a string) if none was passed in. And you also have a variable named rest with an Omit<UserInfo, "email"> value in it (I'm using the Omit<T, K> utility type to represent what happens when you widen a type to forget about some property keys).
If you actually need a value of type Required<UserInfo> (I'm using the Required<T> utility type to represent a type with any optional properties changed to be required), then you'll need to reassemble it from those pieces, such as via shorthand property names in your object literal along with object spreading:
class User {
userInfo: Required<UserInfo>
constructor({ email = "xyz", ...rest }: UserInfo) { // <-- split it apart
this.userInfo = { email, ...rest }; // <-- put it back together
}
}
That works, but it is needlessly destructive (if you'll forgive the pun).
Instead, you can just use spreading to get more or less the same effect:
class User {
userInfo: Required<UserInfo>
constructor(init: UserInfo) {
this.userInfo = { email: "xyz", ...init };
}
}
If the passed-in constructor argument has no email property, then the default one of "xyz" will be there. Otherwise the argument's email property will overwrite the default.
Anyway in both cases you can see that it works as desired:
const u = new User({ userId: "abc" });
console.log(u.userInfo.userId) // abc
console.log(u.userInfo.email) // xyz
const v = new User({ userId: "def", email: "ghi" });
console.log(v.userInfo.userId) // def
console.log(v.userInfo.email) // ghi
Playground link to code
Using typescript, how do extend the User class using Partial<User> as the constructor?
I am also open to solutions which do not use Partial. In this case I am only using the utility type to initialize a blank class. i.e. new User({})
Currently, AdvancedUser only has User properties, but none of the additional advanced?: properties.
export class User {
first_name: string = ''
last_name: string = ''
email: string = ''
constructor(data: Partial<User>) {
Object.assign(this, data)
}
}
export class AdvancedUser extends User {
advanced?: {
foo?: string
}
constructor(data: Partial<User>) {
super(data)
}
}
The provide code actually works. My project was suffering from a downstream typo reverting my AdvancedUser() call back to User().
I am also open to solutions which do not use Partial. In this case I am only using the utility type to initialize a blank class. i.e. new User({})
Instead of having constructors that use Partial, you can get the result you want by using the as keyword, which in my opinion is much cleaner.
As for the advanced property, the reason it's not showing up is because it isn't initialized anywhere (neither inline or in the constructor). Assuming you want to keep it as an optional property, all you need to do is initialize it with undefined:
export class User {
first_name: string = '';
last_name: string = '';
email: string = '';
constructor(data: User) {
Object.assign(this, data);
}
}
export class AdvancedUser extends User {
advanced?: {
foo?: string
} = undefined;
constructor(data: User) {
super(data);
}
}
const au = new AdvancedUser({} as User);
/* OUTPUT:
AdvancedUser: {
"first_name": "",
"last_name": "",
"email": "",
"advanced": undefined
}
*/
console.log(au);
How about something like this for using Partial?:
interface IUserData {
first_name: string;
last_name: string;
email: string;
}
interface IAdvancedUserData {
doAdvancedStuff(IAdvancedStuffOptions) : string;
}
class AdvancedUserData implements IUserData, IAdvancedUserData {
}
your User still accepts data of type Partial, then you pass an AdvancedUserData to your AdvancedUser constructor.
I try to create a user class and want to be able to inherit from a type alias:
type PlainUser = { email: string }
class User extends PlainUser {
constructor (initialValues: PlainUser) {
this.email = initialValues.email
}
update () { ... }
}
This doesn't work of course, but I would like to have the following semantics without having to duplicate email (and all the other fields that I don't show to keep it brief):
type PlainUser = { email: string }
class User {
email: string
constructor (initialValues: PlainUser) {
this.email = initialValues.email
}
update () { ... }
}
Is this possible with flow?
Not that I know of, but you can at least use implements to enforce that the User class implements the PlainUser interface (yes, you have to change it to be an interface).
interface PlainUser {
email: string;
}
class Foo implements PlainUser {
}
(tryflow)
The code above yields the following error with Flow v0.41, since Foo does not specify an email property:
7: class Foo implements PlainUser {
^ property `email` of PlainUser. Property not found in
7: class Foo implements PlainUser {
^ Foo
Of course, this isn't exactly what you've asked for. But at least you are getting automatic checking that User implements PlainUser, rather than nothing at all.
You can only extend from classes, and your type alias is an interface, so you have to use implement here. TypeScript Salsa allows doing the following since this suggestion was implemented:
type PlainUser = { email: string };
class User implements PlainUser {
constructor (initialValues: PlainUser) {
this.email = initialValues.email;
}
}
If you do not use salsa, you have to explicitly declare the inherited properties:
type PlainUser = { email: string };
class User implements PlainUser {
public email: string;
constructor (initialValues: PlainUser) {
this.email = initialValues.email;
}
}
Playground
I'll admit this was a head scratcher initially, but something like what you want to do is very possible. It does require rethinking the approach a bit.
First, you need to start with the class instead of the object literal. Intuitively this makes sense, as that's also the way javascript works.
class User {
email: string;
}
Next you want to use flow's $Shape transformation. This will cast your type to the enumerable properties of the class.
type PlainUser = $Shape<User>;
const Bob: PlainUser = { email: "bob#bob.com" }
or
const BobProperties: PlainUser = { ...new PlainUserClass("bob#bob.com") }
Finally, extend the User class as normal.
class AdminUser extends User {
admin: true;
}
example
I'm using React.js and Typescript and in the React.js store I store Javascript objects. Sometimes they're sent by the server, so they're just objects, they have no member functions. But I want member functions, so that instead of this:
// Interface and "external" member function for an object in the store:
interface User {
id: UserId;
isAdmin?: boolean;
isModerator?: boolean;
...
}
function isStaff(user: User) {
return user.isAdmin || user.isModerator;
}
if (isStaff(user)) {
showPowerOffDataCenterButton();
}
I can do this:
if (user.isStaff()) {
...
Is there any React or Javascript features or Typescript syntactic-sugar magic that can add member functions to the React store data structures? Please note that the objects are sometimes sent from the server as JSON and parsed with JSON.parse(..), so I don't think I can declare my own Javascript classes and add functions to their .prototype field (becasue I don't control the creation of the objects).
(I'm planning to use Redux later + some immutable-JS library, in case that matters)
Or if not possible, any workarounds?
You could just make a class that contains that information instead of having them in separate functions.
interface UserInfo {
id: string;
isAdmin?: boolean;
isModerator?: boolean;
}
class User {
constructor(private user: UserInfo) { }
public isStaff(): boolean {
return this.user.isAdmin || this.user.isModerator;
}
}
let user: User = new User({ id: "wqe" });
console.log(user.isStaff());
You can also make getters and setters for the properties, so you don't lose expresiveness.
class User {
constructor(private user: UserInfo) { }
public isStaff(): boolean {
return this.user.isAdmin || this.user.isModerator;
}
public get isAdmin() {
return this.user.isAdmin;
}
public set isAdmin(value) {
this.user.isAdmin = value;
}
}
You can then get or set isAdmin as you would on a normal object.
user.isAdmin = false;
You can also enforce that isAdmin can not be set by not making a setter for it. so the User class is immutable.