Excess properties are additional properties that are not defined by the type.
typeXY={x:number;y:number;};constx6={x:1,y:2,};consty6:XY={x:1,y:2,z:3,};// compiler error, Object literal may only specify known properties, and 'z' does not exist in type 'XY'.consty6_2={x:1,y:2,z:3,};// not an error, since the y_2 object has no type annotation XYconstall:XY[]=[x6,y6,y_2];// works, since all elements define all of the properties required by the type XY
The compiler reports errors when object literals with type annotations define additional properties, because this is likely to be a mistake (y6 in the example above).
Excess properties do not cause errors when an object is defined without a type annotation (y6_2 in the example above).
you can use compiler option suppressExcessPropertyErrors to suppress errors related to excess properties.
type unions = extract shared properties into a new type
typePerson={id:number;name:string;age:number;};typeProduct={id:number;name:string;price:number;};typePersonProductTypeUnion=Person|Product;// union typeconstarr:PersonProductTypeUnion[]=[{id:1,name:"product1",price:100,},{id:1,name:"person1",age:100,},];arr.forEach((pp)=>{console.log(pp.id);console.log(pp.name);// above this stage, only name and id are available since these are the shared properties of Person and Productconsole.log(pp.age);//Error: Property 'age' does not exist on type 'PersonProductTypeUnion'.// Property 'age' does not exist on type 'Product'// type guard by checking a propertyif("age"inpp){// type now is: Personconsole.log(pp.age);}else{// type now is: Productconsole.log(pp.price);}});
type guards are used to check that an object of a certain type is being used.
for example, the PersonProductTypeUnion above can have either a Person or Product type. we need to confirm that the object is of the correct type before accessing the properties.
Type intersections combine the features of multiple types, allowing all the features to be used.
it is the opposite of type unions, which remove the features that are not shared between its types.
type intersections = type merge.
typePerson={id:number;name:string;age:number;};typeEmployee={department:string;salary:number;startDate:Date;};typePersonEmployeeIntersection=Person&Employee;// intersection typeconstme:PersonEmployeeIntersection={// must define all properties of Person and Employeeid:1,name:"person1",age:100,department:"IT",salary:100,startDate:newDate(),};
the object that results from the intersection type can fit into any of the types that are combined. eg. PersonEmployeeIntersection can be used as a Person or Employee object.
problem when type intersections have the same property name¶
when the intersected types have property with the same name, the type of the newly intersected property is the intersection between the types of this property, but in JS these properties will override each other.
typePerson={id:number;name:string;age:number;};typeEmployee={id:string;department:string;salary:number;startDate:Date;};typePersonEmployeeIntersection=Person&Employee;// intersection type// compiler will compile the intersection type as:typePersonEmployeeIntersection={id:number&string;// the property that has the issue since, number & string is impossiblename:string;age:number;department:string;salary:number;startDate:Date;};
the compiler will raise an error in such cases if the properties with the shared name are of primitive types.
constp:TPerson={id:1,name:"person1",age:100,};conste:Employee={id:"1",department:"IT",salary:100,startDate:newDate(),};// Error: type '{ id: string; department: string; salary: number; startDate: Date; name: string; age: number; }'// is not assignable to type 'PersonEmployeeIntersection'.// Type '{ id: string; department: string; salary: number; startDate: Date; name: string; age: number; }'// is not assignable to type 'TPerson'.// Types of property 'id' are incompatible. Type 'string' is not assignable to type 'number'.ts(2322)constp_e:PersonEmployeeIntersection={...p,...e,};
but if the properties with the shared name are of type objects, the compiler may not raise any errors.
typeTTPerson={id:{userId:number};name:string;age:number;};typeTTEmployee={id:{userId:number;contractId:string};department:string;salary:number;startDate:Date;};constttp:TTPerson={id:{userId:1},name:"person1",age:100,};consttte:TTEmployee={id:{userId:2,contractId:"1"},department:"IT",salary:100,startDate:newDate(),};constttp_tte:TTPerson&TTEmployee={...ttp,...tte,};console.log({ttp_tte});// id from tte will `completely override` id from ttp, and the compiler won't raise any errors./**{ ttp_tte: { id: { userId: 2, contractId: '1' }, name: 'person1', age: 100, department: 'IT', salary: 100, startDate: 2022-08-29T10:24:31.982Z }}*/
in the example above, id from tte will completely override id from ttp, and the compiler won't raise any errors.
Fix problem when type intersections have the same property name¶
these errors are very hard to debug and detect, and the best solution is to:
avoid using type intersections if there are properties with shared names
when merging use a function that intersect the 2 objects properly, eg. rename the property in question, and use the type returned by this intermediate function.
the solution may be useful if you manually intersect types, but with OOP style, inheritance may cause this to happen.