| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
# Conflicts: # tests/baselines/reference/objectRest.errors.txt # tests/baselines/reference/objectRest.types
|
Would it be worth considering adding type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> as a predefined type and using that here? |
Sorry, something went wrong.
We considered that, but there are too many projects out there that already define their own Omit and we'd end up with duplicate definition issues. |
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe a noLib test would be nice, but it looks good.
Sorry, something went wrong.
| @@ -34,8 +33,6 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(17,9): error TS2701: Th | |||
| ~ | |||
There was a problem hiding this comment.
SHould probably remove this function generic<T ... example since the new test covers exactly the same case.
Sorry, something went wrong.
There was a problem hiding this comment.
Yeah, but no harm in keeping it.
Sorry, something went wrong.
|
|
||
| var o: I = { | ||
| ~ | ||
| !!! error TS2322: Type '{ [x: string]: string | number; }' is not assignable to type 'I'. |
There was a problem hiding this comment.
Any idea what's going on here? I'm not sure why we stopped issuing the per-property error.
Sorry, something went wrong.
There was a problem hiding this comment.
I assumed it was the change in looking up the type of property names.
Sorry, something went wrong.
There was a problem hiding this comment.
It's because we previously treated all computed properties as having known (unit type) names, when here they actually have type string. Since they now have type string we generate a string index signature, and the elaboration logic stops trying to drill down to the properties.
Sorry, something went wrong.
# Conflicts: # src/compiler/checker.ts
|
Is this scenario is not supported? Or this is a bug? function foo<T>(obj: T & { x: string }): T {
const { x, ...rest } = obj;
return rest; // Error: [ts] Type 'Pick<T & { x: string; }, Exclude<keyof T, "x">>' is not assignable to type 'T'. [2322]
} |
Sorry, something went wrong.
|
Veniamin Krol (@vkrol) if T contains a property x it will not be present after the destructuring. Therefore the error is correct. |
Sorry, something went wrong.
|
Klaus Meinhardt (@ajafff) But T does not contains x, T & { x: string } contains x 🤔. |
Sorry, something went wrong.
|
Veniamin Krol (@vkrol) Let T={ x: number }. Now T contains x. Klaus Meinhardt (@ajafff) While this is correct, I think it’s actually undesired. Most of the motivation for the higher-order definition of spread as intersection was that it’s quite usable, as well as correct [1] for the common case where two disjoint types are spread together (the case here) or the same type is spread into itself. The definition of rest we ended up with is actually more correct, as you note, but it means that it doesn’t have the same assignability behaviour as spread. I think the correct fix here would be to add a simplification rule in assignability checking that says that Pick<T & U, Exclude<keyof T, keyof U>> is assignable to T. However, I’m not sure if such a rule would be applicable anywhere else, and I think it would be fiddly to get right since U and keyof U are not tightly linked, so you’d have to relate them via assignability instead of just checking type equality. [1] except of course for own-ness, which the compiler doesn’t track well anyway. |
Sorry, something went wrong.
|
What about following scenario Nathan Shively-Sanders (@sandersn) ( related to react HoC, but the same behaviour happens with raw functions ) Fix: It needs to be casted to original P to get rid of errors, although InjectedProps are subtracted from P constraint within the implementation and the exact shape is properly passed via const injectedProps So is this correct behaviour or a bug ? |
Sorry, something went wrong.
|
As far as I understand the problem, I think it's the same as Veniamin Krol (@vkrol)'s. I'm not sure I understand it, however. If InjectedProps is the type that's related to withCounter, why does counterWannabe have to declare it as a type at all? Shouldn't extendedFunc be the one with the dependency on the counter-related types InjectedProps and ExtendedProps? In other words, why does P extend InjectedProps? Shouldn't it be unconstrained and then innerFunc be (props: P & ExtendedProps) => P & InjectedProps ? Of course, given the const { maxCount, ...passThroughProps } = props and the problem Veniamin Krol (@vkrol) lays out above, you'll get Pick<P & ExtendedProps, Exclude<keyof P & ExtendedProps, keyof ExtendedProps>> instead of P for the type of passThroughProps. |
Sorry, something went wrong.
|
Nathan Shively-Sanders (@sandersn) I wanted to simplify the example by not involving react but I guess that was bad move :D sorry about that. So here is the React HoC example, with comments: update 11/22/18:
import React, { Component } from 'react'
import { Counter } from './counter-render-prop'
import { Subtract } from '../types'
type ExtractFuncArguments<T> = T extends (...args: infer A) => any ? A : never;
// get props that Counter injects via children as a function
// InjectedProps is gonna be:
// { count: number; } & { inc: () => void; dec: () => void; }
type InjectedProps = ExtractFuncArguments<Counter['props']['children']>[0];
// withCounter will enhance returned component by ExtendedProps
type ExtendedProps = { maxCount?: number };
// P is constrained to InjectedProps as we wanna make sure that wrapped component
// implements this props API
const withCounter = <P extends InjectedProps>(Cmp: React.ComponentType<P>) => {
class WithCounter extends Component<
// enhanced component will not include InjectedProps anymore as they are injected within render of this HoC and API surface is gonna be extended by ExtendedProps
Subtract<P, InjectedProps> & ExtendedProps
> {
static displayName = `WithCounter(${Cmp.name})`;
render() {
const { maxCount, ...passThroughProps } = this.props;
return (
// we use Counter which has children as a function API for injecting props
<Counter>
{(injectedProps) =>
maxCount && injectedProps.count >= maxCount ? (
<p className="alert alert-danger">
You've reached maximum count! GO HOME {maxCount}
</p>
) : (
// here cast to as P is needed otherwise compile error will occur
<Cmp {...injectedProps} {...passThroughProps as P} />
)
}
</Counter>
);
}
}
return WithCounter;
};
// CounterWannabe implement InjectedProps on it's props
class CounterWannabe extends Component<
InjectedProps & { colorType?: 'primary' | 'secondary' | 'success' }
> {
render() {
const { count, inc, colorType } = this.props;
const cssClass = `alert alert-${colorType}`;
return (
<div style={{ cursor: 'pointer' }} className={cssClass} onClick={inc}>
{count}
</div>
);
}
}
// if CounterWannabe would not implement InjectedProps this line would get compile error
const ExtendedComponent = withCounter(CounterWannabe); |
Sorry, something went wrong.
|
Martin Hochel (@Hotell) What's the source of Counter? I'm not that familiar with React, so I can't guess how the children of Counter are supposed to be typed. |
Sorry, something went wrong.
|
I read the intro to HOCs and I think I understand what's going on, although I haven't had a chance to play with the whole example without Counter.
|
Sorry, something went wrong.
Sorry about that, it's not really important I should explicitly provide props in that example. 👉 I've updated previous comment type State = typeof initialState
type Props = {
children: (
props: State & { inc: Counter['handleInc']; dec: Counter['handleDec'] }
) => React.ReactChild
count?: number
} & typeof defaultProps
const initialState = { count: 0 }
const defaultProps = {
onChange: (value: number) => {}
}
export class Counter extends Component<Props, State> {
static defaultProps = defaultProps
render(){ /*...*/ }
}
My bad, updated the code above
will produce the same error
Thanks a lot Nathan Shively-Sanders (@sandersn) 💪 |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
With this PR we permit rest properties in destructurings of objects of generic types. This effectively implements what is suggested in #10727 (although by different means) and complements our support for generic spread expressions in object literals implemented in #28234.
When a destructuring of an object of a generic type includes a rest variable, the type of the rest variable is an instantiation of the Pick and Exclude predefined types:
Some additional examples: