Why is type not equal to recomposed-decomposed itself? - typescript-typings

I have deconstructed params:T into a + ...restParam, why can't I construct it back into T?
Essentially, why isn't this true:
T === Omit<T, "a"> & { a: number }
where T extends { a: number}?
function foo<T extends { a: number }>(param: T) {
const { a, ...restParam } = param;
const t1: T = {a, ...restParam} ; // <-- getting the following error here
}
TS2322: Type '{ a: number; } & Omit<T, "a">' is not assignable to type 'T'.   '{ a: number; } & Omit<T, "a">' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: number; }'.
I am guessing that T in my case could be more than just a record, e.g. it could be a string that also has a: number property. In which case destructuring param will loose that type information.
Is there a way to declare my foo function to only allow T that's a record (hashtable)?

Related

Assigning an additional type to an object property [duplicate]

Is there a way to change the type of interface property defined in a *.d.ts in typescript?
for example:
An interface in x.d.ts is defined as
interface A {
property: number;
}
I want to change it in the typescript files that I write to
interface A {
property: Object;
}
or even this would work
interface B extends A {
property: Object;
}
Will this approach work? It didn't work when I tried on my system. Just want to confirm if it's even possible?
I use a method that first filters the fields and then combines them.
reference Exclude property from type
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
for interface:
interface A {
x: string
}
interface B extends Omit<A, 'x'> {
x: number
}
type ModifiedType = Modify<OriginalType, {
a: number;
b: number;
}>
interface ModifiedInterface extends Modify<OriginalType, {
a: number;
b: number;
}> {}
Inspired by ZSkycat's extends Omit solution, I came up with this:
type Modify<T, R> = Omit<T, keyof R> & R;
// before typescript#3.5
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R
Example:
interface OriginalInterface {
a: string;
b: boolean;
c: number;
}
type ModifiedType = Modify<OriginalInterface , {
a: number;
b: number;
}>
// ModifiedType = { a: number; b: number; c: number; }
Going step by step:
type R0 = Omit<OriginalType, 'a' | 'b'> // { c: number; }
type R1 = R0 & {a: number, b: number } // { a: number; b: number; c: number; }
type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
type T1 = Pick<OriginalType, T0> // { c: number; }
type T2 = T1 & {a: number, b: number } // { a: number; b: number; c: number; }
TypeScript Utility Types
Deep Modification v3
interface Original {
a: {
a: string
b: { a: string }
c: string
d: string // <- keep this one
}
}
interface Overrides {
a: {
a: { a: number } // <- overwrite string with object
b: number // <- overwrite object with number
c: number // <- overwrite string with number
e: number // <- new property
}
}
type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
const example: ModifiedType = {
a: {
a: { a: number },
b: number,
c: number,
d: string,
e: number,
}
}
Find ModifyDeep below.
You can't change the type of an existing property.
You can add a property:
interface A {
newProperty: any;
}
But changing a type of existing one:
interface A {
property: any;
}
Results in an error:
Subsequent variable declarations must have the same type. Variable
'property' must be of type 'number', but here has type 'any'
You can of course have your own interface which extends an existing one. In that case, you can override a type only to a compatible type, for example:
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
By the way, you probably should avoid using Object as a type, instead use the type any.
In the docs for the any type it states:
The any type is a powerful way to work with existing JavaScript,
allowing you to gradually opt-in and opt-out of type-checking during
compilation. You might expect Object to play a similar role, as it
does in other languages. But variables of type Object only allow you
to assign any value to them - you can’t call arbitrary methods on
them, even ones that actually exist:
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
The short answer for lazy people like me:
type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> };
interface Overrided extends Omit<YourInterface, 'overrideField'> {
overrideField: <type>
}
Extending #zSkycat's answer a little, you can create a generic that accepts two object types and returns a merged type with the members of the second overriding the members of the first.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
interface A {
name: string;
color?: string;
}
// redefine name to be string | number
type B = Merge<A, {
name: string | number;
favorite?: boolean;
}>;
let one: A = {
name: 'asdf',
color: 'blue'
};
// A can become B because the types are all compatible
let two: B = one;
let three: B = {
name: 1
};
three.name = 'Bee';
three.favorite = true;
three.color = 'green';
// B cannot become A because the type of name (string | number) isn't compatible
// with A even though the value is a string
// Error: Type {...} is not assignable to type A
let four: A = three;
Omit the property when extending the interface:
interface A {
a: number;
b: number;
}
interface B extends Omit<A, 'a'> {
a: boolean;
}
I have created this type that allows me to easily override nested interfaces:
export type DeepPartialAny<T> = {
[P in keyof T]?: T[P] extends Obj ? DeepPartialAny<T[P]> : any;
};
export type Override<A extends Obj, AOverride extends DeepPartialAny<A>> = { [K in keyof A]:
AOverride[K] extends never
? A[K]
: AOverride[K] extends Obj
? Override<A[K], AOverride[K]>
: AOverride[K]
};
And then you can use it like that:
interface Foo {
Bar: {
Baz: string;
};
}
type Foo2 = Override<Foo, { Bar: { Baz: number } }>;
const bar: Foo2['Bar']['Baz'] = 1; // number;
For narrowing the type of the property, simple extend works perfect, as in Nitzan's answer:
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
For widening, or generally overriding the type, you can do Zskycat's solution:
interface A {
x: string
}
export type B = Omit<A, 'x'> & { x: number };
But, if your interface A is extending a general interface, you will lose the custom types of A's remaining properties when using Omit.
e.g.
interface A extends Record<string | number, number | string | boolean> {
x: string;
y: boolean;
}
export type B = Omit<A, 'x'> & { x: number };
let b: B = { x: 2, y: "hi" }; // no error on b.y!
The reason is, Omit internally only goes over Exclude<keyof A, 'x'> keys which will be the general string | number in our case. So, B would become {x: number; } and accepts any extra property with the type of number | string | boolean.
To fix that, I came up with a different OverrideProps utility type as following:
type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };
Example:
type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };
interface A extends Record<string | number, number | string | boolean> {
x: string;
y: boolean;
}
export type B = OverrideProps<A, { x: number }>;
let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!
Date: 19/3/2021.
I think the latest typescript(4.1.2) version is supporting interface override in d.ts file.
// in test.d.ts
interface A {
a: string
}
export interface B extends A {
a: number
}
// in any ts file
import { B } from 'test.d.ts'
// this will work
const test: B = { a: 3 }
// this will not work
const test1: B = { a: "3" }
Override as Alias Type
You can use this type alias :
type Override<T, K extends { [P in keyof T]: any } | string> =
K extends string
? Omit<T, K>
: Omit<T, keyof K> & K;
and use alike below syntax :
Global Interface
interface IFirst {
username: string;
}
Interface by override the just name
interface ISecond extends Override<IFirst, 'username'> {
username: number;
}
Type alias override
type IThird = Override<IFirst, { username: boolean }>;
EDIT :
I'm tried to add this alias type as build-in type in typescript by send issue as proposal to the Typescript Repo
Solution for overwriting two or more properties of an interface:
interface Original {
a: string;
b: string;
c: string;
}
interface Modified extends Omit<Original, 'a' | 'b'> {
a?: string; // make it optional
b: boolean; // make it boolean
d: number; // add another property
}
From TypeScript documentation
It's funny I spend the day investigating possibility to solve the same case.
I found that it not possible doing this way:
// a.ts - module
export interface A {
x: string | any;
}
// b.ts - module
import {A} from './a';
type SomeOtherType = {
coolStuff: number
}
interface B extends A {
x: SomeOtherType;
}
Cause A module may not know about all available types in your application. And it's quite boring port everything from everywhere and doing code like this.
export interface A {
x: A | B | C | D ... Million Types Later
}
You have to define type later to have autocomplete works well.
So you can cheat a bit:
// a.ts - module
export interface A {
x: string;
}
Left the some type by default, that allow autocomplete works, when overrides not required.
Then
// b.ts - module
import {A} from './a';
type SomeOtherType = {
coolStuff: number
}
// #ts-ignore
interface B extends A {
x: SomeOtherType;
}
Disable stupid exception here using #ts-ignore flag, saying us the we doing something wrong. And funny thing everything works as expected.
In my case I'm reducing the scope vision of type x, its allow me doing code more stricted. For example you have list of 100 properties, and you reduce it to 10, to avoid stupid situations
If someone else needs a generic utility type to do this, I came up with the following solution:
/**
* Returns object T, but with T[K] overridden to type U.
* #example
* type MyObject = { a: number, b: string }
* OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
*/
export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };
I needed this because in my case, the key to override was a generic itself.
If you don't have Omit ready, see Exclude property from type.
If you only want to modify the type of an existing property and not remove it, then & is enough:
// Style that accepts both number and percent(string)
type BoxStyle = {
height?: string | number,
width?: string | number,
padding?: string | number,
borderRadius?: string | number,
}
// These are both valid
const box1: BoxStyle = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
const box2: BoxStyle = {height: 85, width: 85, padding: 0, borderRadius: 5}
// Override height and width to be only numbers
type BoxStyleNumeric = BoxStyle & {
height?: number,
width?: number,
}
// This is still valid
const box3: BoxStyleNumeric = {height: 85, width: 85, padding: 0, borderRadius: 5}
// This is not valid anymore
const box4: BoxStyleNumeric = {height: '20%', width: '20%', padding: 0, borderRadius: 5}
Try this:
type Override<T extends object, K extends { [P in keyof T]?: any }> = Omit<T, keyof K> & K;
Usage:
type TransformedArticle = Override<Article, { id: string }>;
extending Qwerty's Modify utility type solution to restrict keys of R to ones present in T and add IntelliSense as well
export type Modify<T, R extends Partial<Record<keyof T, any>>> = Omit<T, keyof R> & R;
Deep modification v3
*note, version 2 is in the history of this answer.
interface Original {
a: {
a: string
b: { a: string }
c: string
d: string // <- keep this one
}
}
interface Overrides {
a: {
a: { a: number } // <- overwrite string with object
b: number // <- overwrite object with number
c: number // <- overwrite string with number
e: number // <- new property
}
}
type ModifiedType = ModifyDeep<Original, Overrides>
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
Result
const example: ModifiedType = {
a: {
a: { a: number },
b: number,
c: number,
d: string,
e: number,
}
}
The code
type ModifyDeep<A, B extends DeepPartialAny<A>> = {
[K in keyof A | keyof B]: // For all keys in A and B:
K extends keyof A // ───┐
? K extends keyof B // ───┼─ key K exists in both A and B
? A[K] extends AnyObject // │ ┴──┐
? B[K] extends AnyObject // │ ───┼─ both A and B are objects
? ModifyDeep<A[K], B[K]> // │ │ └─── We need to go deeper (recursively)
: B[K] // │ ├─ B is a primitive 🠆 use B as the final type (new type)
: B[K] // │ └─ A is a primitive 🠆 use B as the final type (new type)
: A[K] // ├─ key only exists in A 🠆 use A as the final type (original type)
: B[K] // └─ key only exists in B 🠆 use B as the final type (new type)
}
type AnyObject = Record<string, any>
// This type is here only for some intellisense for the overrides object
type DeepPartialAny<T> = {
/** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
[P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
}
*Note, type DeepPartialAny is there just for type hints, but it's not perfect. Technically, the logic of the ModifyDeep type allows to replace leaf nodes {a: string} with objects {a: {b: ... }} and vice versa, but DeepPartialAny will complain when overriding an object with a flat primitive with an error such as this
Type 'number' has no properties in common with type 'DeepPartialAny<{ a: string; }>'
However, you can safely ignore the error (with /// #ts-ignore or remove extends DeepPartialAny constraint altogether. The resulting type is computed correctly anyway.
example
TypeScript Playground
type ModifyDeep<A, B extends DeepPartialAny<A>> = {
[K in keyof A | keyof B]:
K extends keyof A
? K extends keyof B
? A[K] extends AnyObject
? B[K] extends AnyObject
? ModifyDeep<A[K], B[K]>
: B[K]
: B[K]
: A[K]
: B[K]
}
type AnyObject = Record<string, any>
type DeepPartialAny<T> = {
/** Makes each property optional and turns each leaf property into any, allowing for type overrides by narrowing any. */
[P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any
}
interface Original {
a: {
a: string
b: { a: string }
c: { a: string }
}
b: string
c: { a: string }
}
interface Overrides {
a: {
a: { a: number } // <- overwrite string with object
b: number // <- overwrite object with number
c: { b: number } // <- add new child property
d: number // <- new primitive property
}
d: { a: number } // <- new object property
}
//#ts-ignore // overriding an object with a flat value raises an error although the resulting type is calculated correctly
type ModifiedType = ModifyDeep<Original, Overrides>
//#ts-ignore
interface ModifiedInterface extends ModifyDeep<Original, Overrides> {}
// Try modifying the properties here to prove that the type is working
const t: ModifiedType = {
a: {
a: { a: 0 },
b: 0,
c: { a: '', b: 0},
d: 0,
},
b: '',
c: { a: '' },
d: { a: 0 },
}
NOTE: Not sure if the syntax I'm using in this answer was available when the older answers were written, but I think that this is a better approach on how to solve the example mentioned in this question.
I've had some issues related to this topic (overwriting interface properties), and this is how I'm handling it:
First create a generic interface, with the possible types you'd like to use.
You can even use choose a default value for the generic parameter as you can see in <T extends number | SOME_OBJECT = number>
type SOME_OBJECT = { foo: "bar" }
interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
property: T;
}
Then you can create new types based on that contract, by passing a value to the generic parameter (or omit it and use the default):
type A_NUMBER = INTERFACE_A; // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT> // MAKES { property: SOME_OBJECT }
And this is the result:
const aNumber: A_NUMBER = {
property: 111 // THIS EXPECTS A NUMBER
}
const anObject: A_SOME_OBJECT = {
property: { // THIS EXPECTS SOME_OBJECT
foo: "bar"
}
}
Typescript playground
Based on ZSkycat's excellent answer, you can create an abstracted Override generic type that is handy to use and explains clearly the intent of the code.
type Override<T, K extends keyof T, N> = Omit<T, K> & { [K1 in K]: N };
where:
T = existing type
K = key of type you wish to override
N = new type for key of existing type to override
Example usage:
type GraphQLCodegenConfig = Override<CodegenConfig, 'schema', DocumentNode>;
Create A modifier Type
type Modify<T, R extends {[P in keyof T]:any} > = Omit<T, keyof R> & R;
and you can
interface ModifiedInterface extends Modify<OriginalType, {
a: number;
b: number;
}> {}
it will give you a type autocomplete
Better solution would be to use below Modified type(pun intended) of this answer
export type Modify<T, R extends Partial<T>> = Omit<T, keyof R> & R;
This will also check that keys you are overriding is also present in the original interface and hence make sure that if original interface changes the name then you will get the compile time error and you also have to change the name.
Explanation:
Take the following example.
interface OriginalInterface {
id: string
}
and modified type is as below
interface ModifiedInterface {
id: number
}
Now, let say in future, OriginalInterface's id gets renamed to uId then using my type utility you will get the error as below
interface ModifiedInterface {
id: number // Type '{ geo_point1: GeoPoint | null; }' has no properties in common with type 'Partial<Address>'.ts(2559)
}

How can I covert an object each filed to an array in a class in typescript

For example, I want to convert a class object into another type. Both in type system and runtime object.
I find the Mapped-Type work well for the type, but I can not find a way to create a new runtime object.
type Primitive = string | number | bigint | boolean | null | undefined;
type DeepArrayObject<T> = {[K in keyof T]-?: DeepArray<T[K]>};
type DeepArray<T> = [T] extends [Primitive]
? T[]
: T extends Array<infer T>
? T[]
: T extends {}
? DeepArrayObject<T>
: T;
function convert<T>(o: T): DeepArray<T> {
// How to create some logic, for example covert each primitive into one lenght array.
}
If I have an object with some type, is it possible?
type MyType = {
a: number;
b?: string;
c: bigint[];
d: {
e: number;
f: string;
};
}
const obj: MyType = {
a: 1,
b: 'Hello',
c: [BigInt(3), BigInt(4)],
d: {
e: 1,
f: 'World'
}
}
const newObj = convert(obj);

is it possible to skip depth level in typescript?

I recently started doing typescript and I was curious about skipping level depth,
is it possible to skip depth level in typescript?
// from
A -> B -> C
// to
A -> C
const sample = {
A: {
B: {
C: "one",
}
},
};
type Make<A, Z> = {
[B in keyof A]: {
[C in keyof A[B]]: {
[D in keyof A[B][C]]: Z;
}
};
};
function foo<T>(object: T): Make<T, number> {
return object as any;
}
// outputs: one
console.log(foo(sample).A.B.C);
what if we want to skip one level like this?
// expected output: one
console.log(foo(sample).A.C);
I tried to do this but it didn't work
type Make2<A, Z> = {
[B in keyof A]: {
[D in keyof A[B][]]: Z;
};
};
function foo2<T>(object: T): Make2<T, number> {
return object as any;
}
// doesnt work
console.log(foo2(sample).A.C);
You can simply skip the middle layer by using key remapping and conditional typing.
We iterate over the values of the object with the remapping accessor in [K in keyof T], checking if its children T[K] are objects, if so, we return the inferred child of the T[K], Item, otherwise we just return T[K]
type SkipFirstDepth<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Record<string, infer Item> ? Item : T[K];
};
type Make = SkipFirstDepth<typeof sample>
Then it's typing will be
{
A: {
C: string;
};
}
Alternatively, if you want to skip every other layer all the way down you'll have to use recursive types.
type SkipEveryOtherDepth<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Record<string, infer Item>
? Item extends Record<string, any>
? SkipEveryOtherDepth<Item>
: Item
: T[K];
};
type Example = SkipEveryOtherDepth<{
foo: {
bar: {
foo: {
bar: {
foo: 'bar';
};
};
};
};
}>;
Then the final type will be
{
foo: {
foo: {
foo: 'bar';
};
};
}
I did this off the cuff, so it may not work in every object shape but definetly works in these given examples.
Check these examples in action at the playground

Typescript two generics that are compatible in function

So I have a function that accepts two callbacks with single arguments, like this:
function f<T1, T2>(a: (input: T1) => number, b: (input: T2) => number) {}
What is want is that T1 and T2 should have such a relation that T1 is assignable to T2. That is, I want it to accept such call:
f((input: {a: number, b: number}) => 2, (input: {a: number}) => 4);
as {a: number, b: number} is assignable to {a: number}. But such call should not compile:
f((input: {}) => 2, (input: {a: number}) => 4);
What I tried doing is rewriting my function declaration in the following way:
function f<T1 extends T2, T2>(a: (input: T1) => number, b: (input: T2) => number) {}
But somehow the second example still gets compiled. Somehow, Typescripts assigns type {a: number} to T1 as well.....
The main reason you're running into trouble is that function types are contravariant in the types of their parameters (see Difference between Variance, Covaraince, Contravariance and Bivariance in TypeScript for more details). For example:
const contravariantFuncParam: (a: { x: string, y: number }) => void =
(a: { x: string }) => { }; // okay
// {x: string} is assignable to {x: string, y: number}
const notCovariantFuncParam: (a: { x: string }) => void =
(a: { x: string, y: number }) => { }; // error!
// {x: string, y: number} is not assignable to {x: string}
So for the following definition,
function f<T1 extends T2, T2>(a: (input: T1) => number, b: (input: T2) => number) { }
the compiler happily accepts this:
f((input: {}) => 2, (input: { a: number }) => 4);
Roughly, the type T2 is inferred from the input to the b callback as {a: number}. Then the compiler knows that T1 is constrained to {a: number}. Since function types are contravariant in their parameters, the compiler sees that (input: {}) => number is assignable to (input: {a: number}) => number, and thus the a input is accepted. And T1 falls back to the constraint {a: number}.
You'd prefer T2 and T1 to be inferred from b and a directly, and then only later enforce T1 extends T2. But by having T1 constrained to T2 this doesn't happen.
One way to do this is to move the T1 extends T2 clause out of the constraint position, and instead use a conditional type for the input to a:
function f<T1, T2>(
a: (input: T1 extends T2 ? T1 : unknown) => number,
b: (input: T2) => number
) { }
f((input: { a: number, b: number }) => 2, (input: { a: number }) => 4); // okay
f((input: {}) => 2, (input: { a: number }) => 4); // error
This works because when faced with inferring from a conditional type like A extends B ? C : D, the compiler will plug in its inferred type for A and then evaluate it. So T1 extends T2 ? T1 : unknown will cause the compiler to infer T1 as the input type to the passed-in a callback. If T1 extends T2 is true, then this is a no-op and the type for input evaluates to T1. Otherwise, the type for input will evaluate to unknown... which, by contravariance of function parameters, will almost certainly fail to accept whatever was passed in.
At this point I see that the two type parameters aren't really necessary, at least for this example. If you could infer T1 from a and not from b, then later go back and just check that T1 is usable as b's callback parameter type (which you used to call T2). If so, then (input: T2)=> number is assignable to (input: T) => number, and thus (by contravariance of function arguments) that T1 extends T2. So let's get rid of T2and just useTinstead ofT1`.
The tricky part here is how to tell the compiler that you want T to be inferred from a and not inferred from b. For b's input type, you want a NoInfer<T> where NoInfer means "just check this, don't use it to infer". If we had such a thing, you could write f() like this:
function f<T>(a: (input: T) => number, b: (input: NoInfer<T>) => number) { }
There is a feature request at microsoft/TypeScript#14829 for a way to mark non-inferential type parameter usage. No official solutions exist, but there are a few suggestions that work for some use cases, like type NoInfer<T> = T & {} from here. I tend to use this one:
type NoInfer<T> = [T][T extends any ? 0 : never];
Using this definition leads to your desired behavior:
f((input: { a: number, b: number }) => 2, (input: { a: number }) => 4); // okay
f((input: {}) => 2, (input: { a: number }) => 4); // error
Both of the above solutions are using tricks/workarounds to try to alter the compiler's type inference behavior. And these tricks are not guaranteed to work in all possible circumstances. Conditional type inference and NoInfer implementations have their pitfalls. So for either solution I strongly recommend extensive testing against likely use cases.
Playground link to code

Typescript Object is of type unknown

How to define the data type of value in the nodejs function written in typescript, so that error of Object is of type unknow can be solved ?
You need to implement type for value. Try smth like this:
type valueType = {
id: string,
field1: number,
field2: string,
}
const obj = {
a: {
id: '12',
field1: 1,
field2: 'asdas',
}
}
for (const [key, value] of Object.entries(obj)) {
const typedValue: valueType = value;
}

Resources