-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Partial Types #11233
Comments
I thought we are not lumping week type detection in this proposal. so why the change? |
Weak type detection everywhere would be a breaking change, whereas here we have an easy place to restrict the bad assignment. |
Do excess property checks still apply though? |
Of course |
This is not the same as covariance/contravariance #1394 ? |
It is one aspect of covariance I believe (in that I would personally rather take this pragmatic approach to dealing with the usage patterns in JavaScript/TypeScript then wait for a total covariant/contravariant solution. I suspect this would not interfere with an eventual full solution (e.g. support C# type |
On the weak type detection, I assume with something like this: interface State {
foo: string;
}
const a: subset State = {}; // not assignable? The only challenge I see with that is that sometimes that might be unavoidable, but I guess that is an edge case. Also what about something like this: interface State {
foo: string;
bar: { baz: string; };
}
interface OptionalState {
foo?: string;
}
interface LiteralState {
foo: 'bar';
}
interface DeepState {
bar: { baz: 'qat'; };
}
function setState<subset T>(state: T) { }
const a: OptionalState = {};
const b: LiteralState = { foo: 'bar' };
const c: DeepState = { bar: { baz: 'qat' } };
setState(a); // Ok?
setState(b); // Ok?
setState(c); // Ok? |
Can you explain the choice of the name 'subset'? type T = { a: string; b: number; }
type Subset = subset T; // equivalent to { a?: string; b?: number; } The set of values in Union and intersection types are named to describe how their value sets are constructed from the value sets of their constituent types. So it seems strange that |
const a: subset State = {}; // not assignable? I forgot to mention the special case that the empty type is not subject to the "at least one property in common" rule 👍 // Corrected to what I think you meant -- 'subset' is not a legal type parameter prefix
function setState(state: subset State) { }
setState(a); // OK: OptionalState has 'foo' in common with 'State'
setState(b); // Error: type 'string' not assignable to { baz: string }
setState(c); // OK: { bar: { baz: 'qat' } } assignable to { bar: { baz: string } }
Hopefully the intuition is at least apparent - that |
that wouldn't work with generic types. Need to better describe assignability rules, like:
|
Prototype (no generics, no sealedness) is working well Open questions I have when implementing this:
|
My opinions for what they are worth:
Wouldn't these be optional too. Actually that might come in super handy when trying to deal with creating decorated functions: interface Foo {
(): void;
foo: string;
}
const foo: subset Foo = function foo() { };
foo.foo = 'bar';
default export <Foo> foo; I guess though, that raises the question in my head, is a subtype "frozen" at the point it is evaluated and assigned? For example: interface State {
foo: string;
bar: number;
}
let a: subset State = { foo: 'string' };
let b: subset State = { bar: 1 };
let c = a; // what gets inferred?
let a = b; // is this valid?
let b = c; // is this valid?
These should persist unmodified to the
Would it be the same order of precedence as the
We heavily use |
I'm a little bit concerned that the keyword Two "type" operator end with Since @RyanCavanaugh mentioned that What about |
Agree with @tinganho that Maybe also consider |
Well, but that sounds like it is extracting the structure, sort of like I think the objection around |
👍 |
I'm liking I'm also not 100% confident about what should actually be done about |
I have always liked it too, just assumed you shied away from it for good reason. 😆 |
That said, if |
I don't like |
Questions to discuss today
|
|
Sounds like it's decided. I'm curious what the team and/or users think about the inconsistent basis this will introduce for describing TypeScript's type operators (including possible future ones)? What I mean is this. union, intersection, subset and superset are all set theory terms that represent operations on sets. But what sets do they operate on in TypeScript? If they operate on sets of properties:
If they operate on sets of values:
Until now all terminology consistently refered to sets of values (union, intersection, subtype=subset, supertype=superset). Now we have an operator that means the opposite ( Just as union types were followed by intersection types, perhaps subset types will be followed by superset types. By this naming, |
An another confusing thing is that |
I can't quite get my head around it yet but I suspect |
@rob3c I think your understanding is correct. Types can be thought of as a shorthand way of characterising a set of values. For example with Now Having said all that, there is no law saying that terminology must refer to value sets. The bike-shedding in this thread is really about landing on a name that avoids confusion and is reasonably consistent with past and possible future namings. |
Thanks @yortus that cleared it up for me! |
Chiming into the bikeshedding: what about |
We're tentatively pushing this out to the release after 2.1 -- looking at a general solution that would let |
@RyanCavanaugh any hints about what this new syntax will encompass / look like? 😃 |
@RyanCavanaugh will object spread stuffs still hit in |
At last some good news in America! |
@AlexGalays I hope you are talking about anything related to the partial types. |
We'll close this with the assumption that #12114 (which is merged now) addresses all these use cases adequately - chime in with a new issue if that turns out not to be the case. |
Hi @RyanCavanaugh, mapped types are fantastic, but it doesn't seem to address the questions above with deep partial types / deep subsets. Mapped types and The use case is that there is an API I'm building against which allows for object merges of very complex/deep objects. |
type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
}; this works just fine for me. |
This gives zero control over Arrays for instance. It will iterate over their keys and allow you to access their method as nullable ones (concat, reduce, etc) which makes no sense. Same with a Date, etc. |
ok, understood. My use case did require it. You then might be interested in #6606 with its mapping capabilities. |
Also the related PR #17961. In fact, I'm facing the same problem. Will be extremely delighted when a solution becomes available. |
This is a proposal for #4889 and a variety of other issues.
Use Cases
Many libraries, notably React, have a method which takes an object and updates corresponding fields on some other object on a per-key basis. It looks like this
Observations
query
); this is a "shallow" operationUnary
partial T
operatorA type
partial T
behaves as follows:T
, except that those properties are now optionalS
type is only assignable to typepartial T
ifS
has at least one property in common withT
partial (T | U)
is equivalent to(partial T) | (partial U)
partial (T & U)
does not expand (this would interact poorly with rule 2)T
is always a subtype ofpartial T
partial T
is equivalent toT
ifT
isany
,never
,null
, orundefined
More thoughts to come later, likely
The text was updated successfully, but these errors were encountered: