1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use crate::{
Type, TypeExpr,
scope::ScopePointer,
type_expr::{ScopePortal, ScopedTypeExpr},
};
impl<T: Type> TypeExpr<T, ScopePortal<T>> {
/// Returns the keys of the constructor fields if this is a constructor or is inferred to be a constructor. None otherwise.
/// The returned expression is guaranteed not to contain any context sensitive types.
/// # Returns
/// `None` if
/// - the key type is unknown due to uninferred vars.
/// - Intersection or Union with distinct scopes.
pub fn keyof(&self, scope: &ScopePointer<T>) -> Option<(ScopedTypeExpr<T>, ScopePointer<T>)> {
// Normalize here so Index, keyof and TypeParameter don't need to get handled by this function.
match self {
Self::Type(inst) => Some((inst.key_type(None), ScopePointer::clone(scope))),
Self::Constructor { inner, parameters } => {
// Parameters should have been normalized by caller
Some((inner.key_type(Some(parameters)), ScopePointer::clone(scope)))
}
// See tsReference.ts
// @Todo: Test this
// keyof (A|B) = keyof A & keyof B
// This is not exactly what typescript does but close enough.
Self::Union(a, b) => {
let (keyof_a, keyof_a_scope) = a.keyof(scope)?;
let (keyof_b, keyof_b_scope) = b.keyof(scope)?;
if keyof_a.is_never_forever(&keyof_a_scope) {
return Some((keyof_b, keyof_b_scope));
}
if keyof_b.is_never_forever(&keyof_b_scope) {
return Some((keyof_a, keyof_a_scope));
}
Self::intersection(&keyof_a, &keyof_b, &keyof_a_scope, &keyof_b_scope)
}
// See tsReference.ts
// @todo test this
Self::Intersection(a, b) => {
if a.is_never_forever(scope) || b.is_never_forever(scope) {
return Some((Self::Never, ScopePointer::clone(scope)));
}
let (keyof_a, keyof_a_scope) = a.keyof(scope)?;
let (keyof_b, keyof_b_scope) = b.keyof(scope)?;
Some((
Self::Union(
Box::new(Self::ScopePortal {
expr: Box::new(keyof_a),
scope: ScopePortal { portal: keyof_a_scope },
}),
Box::new(Self::ScopePortal {
expr: Box::new(keyof_b),
scope: ScopePortal { portal: keyof_b_scope },
}),
),
ScopePointer::clone(scope),
))
}
Self::Operation { a, b, operator } => {
let a_normalized = a.normalize(scope);
let b_normalized = b.normalize(scope);
T::operation(&a_normalized, operator, &b_normalized).keyof(scope)
}
Self::TypeParameter(param, _infer) => {
// Was:
// if let Some((bound, scope)) = scope.lookup_bound(param) {
// But in the case: <T> <C>
// | keyof T | ----- | C |
//
// C will get inferred using the keyof(bound of T) Even when T is not yet inferred.
if let Some((inferred, scope)) = scope.lookup_inferred(param) { inferred.keyof(&scope) } else { None }
}
Self::ScopePortal { expr, scope } => expr.keyof(&scope.portal),
Self::KeyOf(expr) => expr.keyof(scope),
Self::Index { expr, index } => {
let (index_type, index_scope) = expr.index(index, scope, scope)?;
index_type.keyof(&index_scope)
}
Self::Any => Some((T::keyof_any(), ScopePointer::clone(scope))),
Self::NodeSignature(node_signature) => {
// Customized behavior (defaults to never)
Some((T::keyof_node_signature(node_signature.as_ref()), ScopePointer::clone(scope)))
}
Self::PortTypes(_) => Some((Self::Never, ScopePointer::clone(scope))),
// @todo
Self::Conditional { .. } => Some((Self::Never, ScopePointer::clone(scope))),
Self::Never => Some((Self::Never, ScopePointer::clone(scope))),
}
}
}