Skip to content

Commit

Permalink
feat: Implement property access about non-primitive constraints of IAT (
Browse files Browse the repository at this point in the history
#1067)

**Description:**
```ts
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
    tp = s;
}
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
    tp = s;
}
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
    tp = s;
}
```
  • Loading branch information
sunrabbit123 authored Sep 1, 2023
1 parent f46b724 commit a354f07
Show file tree
Hide file tree
Showing 19 changed files with 268 additions and 48 deletions.
10 changes: 10 additions & 0 deletions crates/stc_ts_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,11 @@ pub enum ErrorKind {
RestParamMustBeLast {
span: Span,
},

// TS2536
CannotBeUsedToIndexType {
span: Span,
},
}

#[cfg(target_pointer_width = "64")]
Expand Down Expand Up @@ -2203,6 +2208,7 @@ impl ErrorKind {

ErrorKind::ImportFailed { .. } => 2305,

ErrorKind::CannotBeUsedToIndexType { .. } => 2536,
_ => 0,
}
}
Expand Down Expand Up @@ -2236,6 +2242,10 @@ impl ErrorKind {
matches!(self, Self::NoSuchType { .. } | Self::NoSuchTypeButVarExists { .. })
}

pub fn is_cannot_be_used_index_ty(&self) -> bool {
self.code() == 2536
}

#[cold]
pub fn flatten(vec: Vec<Error>) -> Vec<Error> {
let mut buf = Vec::with_capacity(vec.len());
Expand Down
35 changes: 35 additions & 0 deletions crates/stc_ts_file_analyzer/src/analyzer/assign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,41 @@ impl Analyzer<'_, '_> {
return Ok(());
}

// function f3<T, U extends T>(x: T, y: U, k: keyof T) {
// x[k] = y[k];
// }
(
Type::IndexedAccessType(IndexedAccessType {
obj_type: box Type::Param(TypeParam { name: lhs_name, .. }),
index_type: lhs_idx,
..
}),
Type::IndexedAccessType(IndexedAccessType {
obj_type:
box Type::Param(TypeParam {
constraint: Some(box Type::Param(TypeParam { name: rhs_name, .. })),
..
}),
index_type: rhs_idx,
..
}),
) if lhs_name.eq(rhs_name) && lhs_idx.is_index() && rhs_idx.is_index() => {
if matches!((
lhs_idx.as_index().map(|ty| ty.ty.normalize()),
rhs_idx.as_index().map(|ty| ty.ty.normalize()),
), (
Some(Type::Param(TypeParam {
name: lhs_ty_param_name, ..
})),
Some(Type::Param(TypeParam {
name: rhs_ty_param_name, ..
})),
) if lhs_ty_param_name.eq(rhs_ty_param_name))
{
return Ok(());
}
}

_ => {}
}

Expand Down
92 changes: 85 additions & 7 deletions crates/stc_ts_file_analyzer/src/analyzer/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2174,14 +2174,93 @@ impl Analyzer<'_, '_> {
..
}) => {
{
// Check for `T extends { a: { x: any } }`
if let Some(constraint) = constraint {
let ctx = Ctx {
ignore_errors: true,
..self.ctx
let validate_type_param = |name: &Id| {
match prop.ty().normalize() {
Type::Param(TypeParam { constraint: Some(ty), .. }) => {
if let Type::Index(Index {
ty: box Type::Param(TypeParam { name: constraint_name, .. }),
..
}) = ty.normalize()
{
if !name.eq(constraint_name) {
return Err(ErrorKind::CannotBeUsedToIndexType { span }.into());
}
}
}

Type::Index(Index {
ty: box Type::Param(TypeParam { name: constraint_name, .. }),
..
}) if !name.eq(constraint_name) => return Err(ErrorKind::CannotBeUsedToIndexType { span }.into()),
_ => {}
}
Ok(())
};

if let Some(ty) = constraint {
if let Type::Param(TypeParam { name: constraint_name, .. }) = ty.normalize() {
match validate_type_param(name) {
Ok(..) => {}
Err(err) => validate_type_param(constraint_name)?,
};
}
} else {
match validate_type_param(name) {
Ok(..) => {}
Err(err) => return Err(err),
};
}
}

// Check for `T extends { a: { x: any } }`
if let Some(constraint) = constraint {
let ctx = Ctx {
ignore_errors: true,
..self.ctx
};

let accessible = matches!(
constraint.normalize(),
Type::Keyword(KeywordType {
kind: TsKeywordTypeKind::TsObjectKeyword,
..
})
) || if let Type::Param(TypeParam {
constraint: Some(ty),
name: origin_name,
..
}) = prop.ty().normalize()
{
if name.eq(origin_name) {
true
} else {
if let Type::Index(Index {
ty: box Type::Param(TypeParam { name: constraint_name, .. }),
..
}) = ty.normalize()
{
!name.eq(constraint_name)
} else {
true
}
}
} else {
true
};

if accessible {
if let Ok(ty) = self.with_ctx(ctx).access_property(span, constraint, prop, type_mode, id_ctx, opts) {
return Ok(ty);
if let Type::IndexedAccessType(IndexedAccessType {
obj_type: box Type::Param(TypeParam { name: constraint_name, .. }),
..
}) = ty.normalize()
{
if name.eq(constraint_name) {
return Ok(ty);
}
} else {
return Ok(ty);
}
}
}
}
Expand Down Expand Up @@ -2220,7 +2299,6 @@ impl Analyzer<'_, '_> {
}

warn!("Creating an indexed access type with type parameter as the object");

return Ok(Type::IndexedAccessType(IndexedAccessType {
span,
readonly: false,
Expand Down
7 changes: 5 additions & 2 deletions crates/stc_ts_file_analyzer/src/analyzer/types/keyof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use stc_ts_errors::{debug::force_dump_type_as_string, DebugExt, ErrorKind};
use stc_ts_type_ops::{is_str_lit_or_union, Fix};
use stc_ts_types::{
Class, ClassMember, ClassProperty, Index, KeywordType, KeywordTypeMetadata, LitType, Method, MethodSignature, PropertySignature,
Readonly, Ref, Type, TypeElement,
Readonly, Ref, Type, TypeElement, TypeParam,
};
use stc_utils::{cache::Freeze, ext::TypeVecExt, stack, try_cache};
use swc_atoms::js_word;
Expand Down Expand Up @@ -313,7 +313,10 @@ impl Analyzer<'_, '_> {
return Ok(Type::new_union(span, key_types));
}

Type::Param(..) => {
Type::Param(TypeParam { constraint, .. }) => {
// if let Some(box c) = constraint {
// return self.keyof(span, c);
// }
return Ok(Type::Index(Index {
span,
ty: Box::new(ty.into_owned()),
Expand Down
7 changes: 7 additions & 0 deletions crates/stc_ts_file_analyzer/src/analyzer/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ impl Analyzer<'_, '_> {
);

if result.is_err() {
match result {
Err(err) if err.is_cannot_be_used_index_ty() => {
return Err(err);
}
_ => (),
}

if let Type::Param(TypeParam { constraint: Some(ty), .. }) = index_ty.normalize() {
let prop = self.normalize(span, Cow::Borrowed(ty), opts)?.into_owned();
result = self.with_ctx(ctx).access_property(
Expand Down
19 changes: 19 additions & 0 deletions crates/stc_ts_file_analyzer/tests/pass-only/types/keyof/3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @strictNullChecks: true
// @declaration: true

class Shape {
name: string;
width: number;
height: number;
visible: boolean;
}

function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}

function f33<S extends Shape, K extends keyof S>(shape: S, key: K) {
let name = getProperty(shape, "name");
let prop = getProperty(shape, key);
return prop;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @strictNullChecks: true
// @declaration: true

function f4<T, U extends T, K extends keyof T>(x: T, y: U, k: K) {
x[k] = y[k];
}
23 changes: 23 additions & 0 deletions crates/stc_ts_file_analyzer/tests/tsc/types/mapped/1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @strictNullChecks: true
// @declaration: true

function f1<T>(x: T, k: keyof T) {
return x[k];
}

function f2<T, K extends keyof T>(x: T, k: K) {
return x[k];
}

function f3<T, U extends T>(x: T, y: U, k: keyof T) {
x[k] = y[k];
}

function f10<T>(x: T, y: Partial<T>, k: keyof T) {
y[k] = x[k];
}

function f12<T, U extends T>(x: T, y: Partial<U>, k: keyof T) {
x[k] = y[k]; // Error
y[k] = x[k]; // Error
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"file": "tests/tsc/types/mapped/1.ts",
"line": 21,
"col": 5,
"code": 2322
},
{
"file": "tests/tsc/types/mapped/1.ts",
"line": 22,
"col": 5,
"code": 2322
}
]
7 changes: 7 additions & 0 deletions crates/stc_ts_file_analyzer/tests/tsc/types/mapped/2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @strictNullChecks: true
// @declaration: true

function f6<T, U extends T, K extends keyof U>(x: T, y: U, k: K) {
x[k] = y[k]; // Error
y[k] = x[k]; // Error
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"file": "tests/tsc/types/mapped/2.ts",
"line": 5,
"col": 5,
"code": 2536
},
{
"file": "tests/tsc/types/mapped/2.ts",
"line": 6,
"col": 12,
"code": 2536
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
}
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
}
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"file": "tests/tsc/types/nonPrimitive/1.ts",
"line": 5,
"col": 5,
"code": 2322
},
{
"file": "tests/tsc/types/nonPrimitive/1.ts",
"line": 8,
"col": 5,
"code": 2322
},
{
"file": "tests/tsc/types/nonPrimitive/1.ts",
"line": 11,
"col": 5,
"code": 2322
}
]
1 change: 1 addition & 0 deletions crates/stc_ts_type_checker/tests/conformance.pass.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2428,6 +2428,7 @@ types/nonPrimitive/nonPrimitiveAndEmptyObject.ts
types/nonPrimitive/nonPrimitiveAndTypeVariables.ts
types/nonPrimitive/nonPrimitiveAsProperty.ts
types/nonPrimitive/nonPrimitiveAssignError.ts
types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts
types/nonPrimitive/nonPrimitiveInFunction.ts
types/nonPrimitive/nonPrimitiveInGeneric.ts
types/nonPrimitive/nonPrimitiveIndexingWithForIn.ts
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
{
"required_errors": {
"TS2322": 12,
"TS2536": 4,
"TS2542": 4
"TS2542": 4,
"TS2322": 10
},
"required_error_lines": {
"TS2542": [
54,
59,
64,
69
],
"TS2322": [
14,
19,
75,
81,
91,
Expand All @@ -18,25 +21,14 @@
161,
166,
171
],
"TS2536": [
23,
24,
28,
29
],
"TS2542": [
54,
59,
64,
69
]
},
"extra_errors": {
"TS2322": 5
"TS2322": 6
},
"extra_error_lines": {
"TS2322": [
18,
39,
58,
59,
Expand Down
Loading

0 comments on commit a354f07

Please sign in to comment.