| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
Awesome, this is my most anticipated feature! One thing I don't quite understand is how will this work with rest spread operator? For example what inferred return type this function will have? function foo<T extends { x: string }>(obj: T) {
const { x, ...rest } = obj;
return rest;
}
|
Sorry, something went wrong.
|
Farkhad Khatamov (@hatamov) This PR doesn't change the behavior of rest properties. However, I'm considering a separate PR that would use the Pick and Exclude predefined types to compute rest types: function foo<T extends { a: string, b: string }>(obj: T) {
let { a, b, ...rest } = obj; // let rest: Pick<Exclude<T, 'a' | 'b'>>
} |
Sorry, something went wrong.
|
Anders Hejlsberg (@ahejlsberg) Would it be possible to use Pick and Exclude to more precisely construct the resulting type of spreading an instantiable type? function spread<T, U>(t: T, u: U) {
return { ...t, ...u }; // Pick<T, Exclude<keyof T, keyof U>> & U
}Of course this needs to be a little bit more complex to correctly handle optional properties. |
Sorry, something went wrong.
It's possible, see for example my comment here. However, it ends up generating very complex types (and commensurately complex error messages), and even then we still lack accurate representation of which properties were declared optional and which properties are "own enumerable" properties. |
Sorry, something went wrong.
There was a problem hiding this comment.
Sorry, something went wrong.
|
Nathan Shively-Sanders (@sandersn) Your comments should be addressed now. In some cases I moved the tests, in others I just updated the comments. |
Sorry, something went wrong.
|
|
||
| function isValidSpreadType(type: Type): boolean { | ||
| return !!(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.NonPrimitive) || | ||
| return !!(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.NonPrimitive | TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) || |
There was a problem hiding this comment.
In this InstantiableNonPrimitive case, shouldn't this be checking that the constraint of type, if it exists, isValidSpreadType?
Sorry, something went wrong.
Is the following behaviour also grouped under this description? function ripoff<T>(augmented: T): T {
const { ...original } = augmented;
return original;
}
interface Foo {
x: number;
(y: string): number;
}
const foo = (x: string) => x.length;
foo.x = 42;
const x: Foo = ripoff(foo);
x("string") // TypeError: x is not a functionDid the team consider returning the type Pick<T, keyof T> instead? |
Sorry, something went wrong.
|
Jack Williams (@jack-williams) Yes, except that's rest, not spread. The principle is the same though. We think that pattern of spreading a callable object is rare, especially in generic functions like ripoff. (Non-generic uses are already correct.) |
Sorry, something went wrong.
|
Nathan Shively-Sanders (@sandersn)
Yep sorry, silly mistake.
I agree that the callable case is rare (and contrived) but there are other object invariants someone might expect to be preserved by a function of type <X>(x: X) => X. The current choice seems to be invested in either having rest preserve all these invariants (current and future new object features) or having the violation of these invariants being suitable rare or low-impact as to not matter. I expect my concerns are unfounded, it just seems that types like Pick are exactly what you want in this situation. |
Sorry, something went wrong.
|
Is this expected? Playground function func2<T extends { a: string, b: string }>(param: T)
: { a: string, b: string } {
const { a, ...rest } = param;
const x = rest.b; // Property 'b' does not exist on type 'Pick<T, Exclude<keyof T, "a">>'.
return { a, ...rest }; // Property 'b' is missing in type '{ a: string; } & Pick<T, Exclude<keyof T, "a">>'
// but required in type '{ a: string; b: string; }'.
} |
Sorry, something went wrong.
|
JasonKleban That was fixed by #29121, give it a try with typescript@next. |
Sorry, something went wrong.
|
A bit of an aside to Anders' comment:
I was looking at typing Object.assign better, and there's a few modifications that could be made to Given: let x_1 = { x: 1 };
let x_true = { x: true };
let x_string = { x: "hello" };
If we wanted to type assign 'more precisely' (but not 'most precisely'?) we could write: const assign3 =
<T, U, V> (first: T, second: U, third: V)
: (Omit<T, keyof (U&V)> & Omit<U, keyof V> & V) =>
Object.assign(first, second, third) as any;
The library Omit type is: type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>> The generated result type looks pretty bad: To improve it, we can handle the "exclude everything" case like so: type Omit<T, K extends keyof any> =
Exclude<keyof T, K> extends never
? {}
: Pick<T, Exclude<keyof T, K>>;
This greatly simplifies the generated type: However, if we add back more properties... let x_1 = { x: 1, j: 2 };
let x_true = { x: true, k: 2 };
let x_string = { x: "hello", l: 2 };
We are again in "scary type" territory: To improve this we can inline Pick into the definition: type Omit<T, K extends keyof any> =
Exclude<keyof T, K> extends never
? {}
: { [k in Exclude<keyof T, K>]: T[k] };
And we end up with something a bit nicer (although there seems to be no way to force & to reduce): I'm not sure if this is worth doing to Omit or if there should be some attribute that can be applied to types like Pick saying that they are "transparent" and should always be fully expanded, never shown. |
Sorry, something went wrong.
interface InputProps {
name: string
value: any
}
interface SelectProps extends InputProps{
items: any[]
}
export const Input = (props : InputProps) => {
return <div>Sono un input</div>
}
export const Select = (props : SelectProps) => {
return <div>Sono un Select</div>
}
type PropsOf<T> = T extends React.ComponentType<infer Props> ? Props : object
// type BPropsOf<T> = PropsOf<BaseFieldProps<T>>
type BaseFieldProps<T> = {
field1: string
field2: boolean
component: React.ComponentType<PropsOf<T>>
} & {
[Property in keyof PropsOf<T>]: PropsOf<T>[Property]
}
// type FieldProps = BaseFieldProps<InputProps> // & PropsOf<BaseFieldProps<InputProps>['component']>
// & PropsOf<FieldProps['component']>
function Field < C >(props : BaseFieldProps<C>){
return <div>Sono un field</div>
}
export const Home = () => {
return (
<div>
<h2>I am body</h2>
<Field<typeof Select>
field1="field1"
field2
component={Select}
{/* Here all the properties of the component */}
/>
</div>
)
}There is a way to avoid having to write the generic "typeof Select" near "Field" and extract it from the prop "component"?? |
Sorry, something went wrong.
- Generic spread expressions for object literals are supported by TypeScript: microsoft/TypeScript#28234
## Explanation
- For runtime property assignment, use `as unknown as` instead of `as
any`.
- Change the types for `BaseControllerV1` class fields `initialConfig`,
`initialState` from `C`, `S` to `Partial<C>`, `Partial<S>`.
- Initial user-supplied constructor options do not need to be complete
`C`, `S` objects, since `internal{Config,State}` will be populated with
`default{Config,State}`.
- For empty objects, prefer no type assertions or `as never` (`never` is
assignable to all types).
- Fix code written based on outdated TypeScript limitation.
- Generic spread expressions for object literals are supported by
TypeScript: microsoft/TypeScript#28234
## References
- Closes #3715
## Changelog
###
[`@metamask/base-controller`](https://github.com/MetaMask/core/pull/3959/files#diff-a8212838da15b445582e5622bd4cc8195e4c52bcf87210af8074555f806706a9)
## Checklist
- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
| Back | FazBrowse Home | New Git URL |
With this PR we permit generic types in spread expressions within object literals. Object literals with generic spread expressions now produce intersection types, similar to the Object.assign function and JSX literals. For example:
Property assignments and non-generic spread expressions are merged to the greatest extent possible on either side of a generic spread expression. For example:
Non-generic spread expressions continue to be processed as before: Call and construct signatures are stripped, only non-method properties are preserved, and for properties with the same name, the type of the rightmost property is used. This contrasts with intersection types which concatenate call and construct signatures, preserve all properties, and intersect the types of properties with the same name. Thus, spreads of the same types may produce different results when they are created through instantiation of generic types:
An alternative to using intersections would be to introduce a higher-order type operator { ...T, ...U } along the lines of what #10727 explores. While this appears attractive at first, it would take a substantial amount of work to implement this new type constructor and endow it with all the capabilities we already implement for intersection types, and it would produce little or no gain in precision for most scenarios. In particular, the differences really only matter when spread expressions involve objects with overlapping property names that have different types. Furthermore, for an unconstrained type parameter T, the type { ...T, ...T } wouldn't actually be assignable to T, which, while technically correct, would be pedantically annoying.
Having explored both options, and given that intersection types are already used for Object.assign and JSX literals, we feel that intersection types strike the best balance between accuracy and complexity for this feature.