microsoft / microsoft/TypeScript

Proposal: allow variance annotations on input positions

Offen
#62,639 4 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Awaiting More Feedback Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
2 T. 4 Std.
Gemergte PRs (30 T.)
132

Beschreibung

### ⭐ Suggestion

I would like the `in` and `out` type modifiers to be able to applied to a generic type when it is being instantiated.

This would function by expanding the type by eliminating fields that are in violation of the requested constraint.

This might be thought of as another form of anonymous type mapping.

For example the code that can currently be written as
```typescript
interface BivariantSetLike {
size: number;
}
interface CovariantSetLike extends BivariantSetLike {
getAll: () => Iterable;
}
interface ContravariantSetLike extends BivariantSetLike {
add: (key: T) => void;
}
interface SetLike
extends CovariantSetLike,
ContravariantSetLike,
BivariantSetLike {
constrain: (key: T) => T | undefined
}

declare const dogs: SetLike;
const animals: CovariantSetLike = dogs;
const poodles: ContravariantSetLike = dogs;
const countable: BivariantSetLike = dogs;
```
Could instead be written as
```typescript
interface SetLike {
size: number;
getAll: () => Iterable;
add: (key: T) => void;
constrain: (key: T) => T | undefined;
}

declare const dogs: SetLike;
const animals: SetLike<-in Animal> = dogs;
const poodles: SetLike<-out Poodle> = dogs;
const countable: SetLike<-in -out void> = dogs;
```

I'm not opposed to allowing the non-negated forms to be used in type instantiations, but in line with the existing pattern that `in` and `out` already don't impose variance restrictions on otherwise variant code: I think the positive forms, if they are even syntactically allowed, should always no-op. So `MyType == MyType == MyType == MyType`

## `readonly` and `writeonly`
`field: -in T` -> `readonly field: T`
`field: -out T` -> `set field(value: T)`
`readonly field: -in T` -> `readonly field: T`
`readonly field: -out T` -> (field is skipped)
`set field(value: -in T)` -> (field is skipped)
`set field(value: -out T)` -> `set field(value: T)`

This would break for an index signature type like `{[n: number]: T}` for `-out T`, because there is no syntax to describe a `writeonly [n: number]`.
Perhaps, instead of simplifying types to remove the `-in ReifiedT`, it is just legal to use the `-in` and `-out` modifiers anywhere a type is legal inside of an object type, and it just impacts the semantics of the type.
If this were the case, it would be legal to write something like `{x: -in number}` as an alias for `{readonly x: number}`. I'm not particularly fond of this option, but it does provide an elegant answer for lacking `writeonly`.

## Strict Methods
TypeScript treats methods and functions differently when it comes to variance (assuming `--strictFunctionTypes`, which is table stakes for any use of the `in out` modifiers anyway). I think the special treatment of methods should be ignored when using this new syntax, as it largely defeats the purpose. So `WritableFunction` and `WritableMethod` will remain distinct, but `T1` and `T2` ought to be be identical:
```typescript
interface WritableFunction {
write: (value: T) => void;
}
interface WritableMethod {
write(value: T): void;
}

type T1 = WritableFunction<-in number>;
type T2 = WritableMethod<-in number>;
```

The alternative is to make a `--strictMethodTypes` flag that enables method type checking everywhere.
This proposal's syntax would, I believe, allow for things like `Array` to be mostly usable, despite their non-variance.

### 🔍 Search Terms

Variance covariance contravariance bivariance in out input

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Das Issue nennt keine Implementierungsdateien oder Tests. Beginne damit, die Pfade für die Typprüfung und die Generics-Instanziierung von TypeScript zu finden, und vergleiche sie anschließend mit den Beispielen zu variance, readonly/writeonly, index-signature und strict-method. Die Aufgabe ist erledigt, wenn eine Einigung über die Semantik der Alternativen erzielt wurde und Compiler-Tests die vorgeschlagene Syntax abdecken.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
compilers
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.