1use std::{fmt, panic::Location, str::FromStr, vec};
2
3mod macros;
4
5use lazy_static::lazy_static;
6use regex::Regex;
7use syn::{GenericArgument, PathArguments, Type};
8
9lazy_static! {
10 static ref OPTION_REGEX: Regex = Regex::new(r"^Option<(.+)>$").unwrap();
12 static ref VEC_REGEX: Regex = Regex::new(r"^Vec<(.+)>$").unwrap();
14}
15
16#[derive(Clone, Eq, PartialEq, Hash)]
22pub enum TsType {
23 Base(String),
25 Paren(Box<TsType>),
26 Array(Box<TsType>),
27 Tuple(Vec<TsType>),
28 Union(Vec<TsType>),
29 Intersection(Vec<TsType>),
30 Generic(Box<TsType>, Vec<TsType>),
32 IndexedAccess(Box<TsType>, Box<TsType>),
34}
35
36impl Default for TsType {
37 fn default() -> Self {
38 TsType::Base("any".to_string())
39 }
40}
41
42pub struct TsTypeError {
45 pub message: String,
46 pub location: String,
47}
48
49impl fmt::Display for TsTypeError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(
52 f,
53 "TypeError: {}\n Location: {}",
54 self.message, self.location
55 )
56 }
57}
58
59impl fmt::Debug for TsTypeError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(
62 f,
63 "TypeError: {}\n Location: {}",
64 self.message, self.location
65 )
66 }
67}
68
69impl TsType {
72 pub fn is_union_with(&self, other: &Self) -> bool {
74 match self {
75 Self::Union(types) => types.iter().any(|ty| ty == other),
76 _ => false,
77 }
78 }
79
80 pub fn contains(&self, other: &Self) -> bool {
83 if self == other {
84 return true;
85 }
86 match self {
87 Self::Base(name) => match other {
88 Self::Base(other_name) => name == other_name,
90 _ => false,
92 },
93 Self::Paren(inner) => inner.contains(other),
94 Self::Array(inner) => inner.contains(other),
95 Self::Tuple(types) => types.iter().any(|ty| ty.contains(other)),
96 Self::Union(types) => types.iter().any(|ty| ty.contains(other)),
97 Self::Intersection(types) => types.iter().any(|ty| ty.contains(other)),
98 Self::Generic(base, args) => {
99 base.contains(other) || args.iter().any(|arg| arg.contains(other))
100 }
101 Self::IndexedAccess(base, key) => base.contains(other) || key.contains(other),
102 }
103 }
104
105 pub fn as_generic(self, args: Vec<Self>) -> Self {
107 match self {
108 Self::Base(_) => Self::Generic(Box::new(self), args),
109 Self::IndexedAccess(_, _) => Self::Generic(Box::new(self), args),
110 _ => panic!("Type can't be generic."),
111 }
112 }
113
114 pub fn property(self, key: Self) -> Self {
116 Self::IndexedAccess(Box::new(self), Box::new(key))
117 }
118
119 pub fn in_array(self) -> Self {
121 Self::Array(Box::new(self))
122 }
123
124 pub fn in_parens(self) -> Self {
126 match self {
127 Self::Intersection(_) => Self::Paren(Box::new(self)),
128 Self::Union(_) => Self::Paren(Box::new(self)),
129 _ => self,
131 }
132 }
133
134 pub fn or(self, other: Self) -> Self {
136 match self {
137 Self::Union(mut types) => match other {
138 Self::Union(mut other_types) => {
140 types.append(&mut other_types);
141 Self::Union(types)
142 }
143 _ => {
145 types.push(other);
146 Self::Union(types)
147 }
148 },
149 _ => match other {
150 Self::Union(mut other_types) => {
152 other_types.insert(0, self);
153 Self::Union(other_types)
154 }
155 _ => Self::Union(vec![self, other]),
157 },
158 }
159 }
160
161 pub fn and(self, other: Self) -> Self {
163 match self {
164 Self::Intersection(mut types) => match other {
165 Self::Intersection(mut other_types) => {
167 types.append(&mut other_types);
168 Self::Intersection(types)
169 }
170 _ => {
172 types.push(other);
173 Self::Intersection(types)
174 }
175 },
176 _ => match other {
177 Self::Intersection(mut types) => {
179 types.insert(0, self);
180 Self::Intersection(types)
181 }
182 _ => Self::Intersection(vec![self, other]),
184 },
185 }
186 }
187
188 pub fn join(self, other: Self) -> Result<Self, &'static str> {
191 match self {
192 Self::Base(mut str) => match other {
193 Self::Base(other_str) => {
195 str.push_str(&other_str);
196 Ok(Self::Base(str))
197 }
198 _ => other.join(Self::Base(str)),
200 },
201 Self::Generic(ty, mut args) => {
203 args.push(other);
204 Ok(Self::Generic(ty, args))
205 }
206 Self::IndexedAccess(object, key) => {
208 let key_inner = *key;
209 Ok(Self::IndexedAccess(
210 object,
211 Box::new(key_inner.join(other)?),
212 ))
213 }
214 Self::Union(mut types) => {
215 match other {
216 Self::Union(mut other_types) => {
218 types.append(&mut other_types);
219 Ok(Self::Union(types))
220 }
221 _ => {
223 types.push(other);
224 Ok(Self::Union(types))
225 }
226 }
227 }
228 Self::Intersection(mut types) => {
229 match other {
230 Self::Intersection(mut other_types) => {
232 types.append(&mut other_types);
233 Ok(Self::Intersection(types))
234 }
235 Self::Union(mut union_types) => {
237 let first_member = union_types.remove(0);
238 let intersection = Self::Intersection(types);
239 let intersected_member = intersection.and(first_member);
240 union_types.insert(0, intersected_member);
241 Ok(Self::Union(union_types))
242 }
243 _ => {
245 types.push(other);
246 Ok(Self::Intersection(types))
247 }
248 }
249 }
250 Self::Tuple(mut types) => {
252 types.push(other);
253 Ok(Self::Tuple(types))
254 }
255 Self::Paren(inner) => inner.join(other),
257 _ => Err("Type does not support joining."),
258 }
259 }
260
261 #[track_caller]
278 pub fn from_ts_str(str: &str) -> Result<Self, TsTypeError> {
279 let location = Location::caller();
280
281 if str.is_empty() {
282 return Err(type_error_at!(location, "Empty string."));
283 }
284
285 let mut stacks: Vec<Vec<Self>> = vec![];
286 let mut pending_stack: Vec<Self> = vec![];
287 let mut pending_type: Option<Self> = None;
288 let mut ambiguous_bracket = false;
289 let chars = str.trim().chars();
290
291 for char in chars {
292 if ambiguous_bracket && char != ']' {
293 pending_stack.push(pending_type.unwrap().property(TsType::Base("".to_string())));
294 pending_type = None;
295 ambiguous_bracket = false;
296 }
297 match char {
298 ' ' => continue,
299 '|' => {
300 let member = match pending_type {
303 Some(ty) => vec![ty],
304 None => vec![],
305 };
306 let mut _union = Self::Union(member);
307 pending_stack.push(_union);
308 pending_type = None;
309 }
310 '&' => {
311 let member = match pending_type {
314 Some(ty) => vec![ty],
315 None => vec![],
316 };
317 let intersection = Self::Intersection(member);
318 pending_stack.push(intersection);
319 pending_type = None;
320 }
321 '<' => {
322 if pending_type.is_none() {
324 return Err(type_error_at!(location, "Unexpected `<` found."));
325 }
326 let inner = pending_type.unwrap();
327 let generic = inner.as_generic(vec![]);
328 pending_stack.push(generic);
329 pending_type = None;
330 }
331 ',' => {
332 if pending_type.is_none() {
335 return Err(type_error_at!(location, "Unexpected `,` found."));
336 }
337 let mut inner = pending_type.unwrap();
338
339 loop {
340 let top = pending_stack.pop().unwrap();
341 inner = top.join(inner).unwrap();
342
343 match inner {
344 Self::Generic(_, _) => break,
345 Self::IndexedAccess(_, _) => break,
346 Self::Tuple(_) => break,
347 _ => {}
348 }
349
350 if pending_stack.is_empty() {
351 return Err(type_error_at!(location, "Unexpected `,` found."));
352 }
353 }
354 pending_stack.push(inner);
355 pending_type = None;
356 }
357 '>' => {
358 if pending_type.is_none() {
361 return Err(type_error_at!(location, "Unexpected `>` found."));
362 };
363 let mut ty = pending_type.unwrap();
364 loop {
365 let top = pending_stack.pop().unwrap();
366 ty = top.join(ty).unwrap();
367
368 if let Self::Generic(_, _) = ty {
369 break;
370 }
371
372 if pending_stack.is_empty() {
373 return Err(type_error_at!(location, "Unexpected `,` found."));
374 }
375 }
376 pending_type = Some(ty);
377 }
378 '[' => {
379 if pending_type.is_none() {
380 let tuple = Self::Tuple(vec![]);
382 pending_stack.push(tuple);
383 } else {
384 ambiguous_bracket = true;
387 }
388 }
389 ']' => {
390 if pending_type.is_none() {
391 return Err(type_error_at!(location, "Unexpected `]` found."));
392 };
393 let mut ty = pending_type.unwrap();
394
395 if ambiguous_bracket {
398 pending_type = Some(ty.in_array());
399 ambiguous_bracket = false;
400 } else {
401 loop {
402 let top = pending_stack.pop().unwrap();
403 ty = top.join(ty).unwrap();
404
405 match ty {
406 Self::IndexedAccess(_, _) => break,
407 Self::Tuple(_) => break,
408 _ => {}
409 }
410
411 if pending_stack.is_empty() {
412 return Err(type_error_at!(location, "Unexpected `]` found."));
413 }
414 }
415 pending_type = Some(ty);
416 }
417 }
418 '(' => {
419 if pending_type.is_some() {
420 return Err(type_error_at!(location, "Unexpected `(` found."));
421 };
422
423 stacks.push(pending_stack);
425 pending_stack = vec![];
426 }
427 ')' => {
428 if pending_type.is_none() {
431 return Err(type_error_at!(location, "Unexpected `)` found."));
432 };
433 let mut inner = pending_type.unwrap();
434
435 for _ in 0..pending_stack.len() {
436 let top = pending_stack.pop().unwrap();
437 inner = top.join(inner).unwrap();
438 }
439
440 pending_type = Some(inner.in_parens());
441 pending_stack = stacks.pop().unwrap();
442 }
443 part => {
444 match pending_type {
445 Some(pending) => {
446 let next = Self::Base(part.to_string());
448 pending_type = Some(pending.join(next).unwrap());
449 }
450 None => {
451 pending_type = Some(Self::Base(part.to_string()));
453 }
454 }
455 }
456 }
457 }
458
459 let mut final_ty = pending_type.unwrap_or_else(|| pending_stack.pop().unwrap());
460
461 while let Some(top) = pending_stack.pop() {
462 final_ty = top.join(final_ty).unwrap();
463 }
464
465 Ok(final_ty)
466 }
467}
468
469impl fmt::Display for TsType {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 match self {
474 TsType::Base(name) => write!(f, "{}", name.trim()),
475 TsType::Array(ty) => match ty.as_ref() {
476 TsType::Union(_) => write!(f, "({})[]", ty),
479 TsType::Intersection(_) => write!(f, "({})[]", ty),
480 _ => write!(f, "{}[]", ty.to_string()),
481 },
482 TsType::Paren(ty) => write!(f, "({})", ty.to_string()),
483 TsType::IndexedAccess(ty, key_ty) => {
484 write!(f, "{}[{}]", ty.to_string(), key_ty.to_string())
485 }
486 TsType::Generic(name, args) => {
487 let args = args
488 .iter()
489 .map(|ty| ty.to_string())
490 .collect::<Vec<_>>()
491 .join(", ");
492 write!(f, "{}<{}>", name, args)
493 }
494 TsType::Union(types) => {
495 let types = types
496 .iter()
497 .map(|ty| match ty {
498 TsType::Intersection(_) => format!("({})", ty),
501 _ => ty.to_string(),
502 })
503 .collect::<Vec<_>>()
504 .join(" | ");
505 write!(f, "{}", types)
506 }
507 TsType::Intersection(types) => {
508 let types = types
509 .iter()
510 .map(|ty| ty.to_string())
511 .collect::<Vec<_>>()
512 .join(" & ");
513 write!(f, "{}", types)
514 }
515 TsType::Tuple(types) => {
516 let types = types
517 .iter()
518 .map(|ty| ty.to_string())
519 .collect::<Vec<_>>()
520 .join(", ");
521 write!(f, "[{}]", types)
522 }
523 }
524 }
525}
526
527impl fmt::Debug for TsType {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 match self {
530 TsType::Base(name) => write!(f, "Base({})", name.trim()),
531 TsType::Array(ty) => write!(f, "Array({:?})", ty),
532 TsType::Paren(ty) => write!(f, "Paren({:?})", ty),
533 TsType::IndexedAccess(ty, key_ty) => {
534 write!(f, "IndexedAccess({:?}[{:?}])", ty, key_ty)
535 }
536 TsType::Generic(name, args) => {
537 write!(f, "Generic(")?;
538 write!(f, "{:?}<", name)?;
539
540 for (i, arg) in args.iter().enumerate() {
541 write!(f, "{:?}", arg)?;
542 if i < args.len() - 1 {
543 write!(f, ", ")?;
544 }
545 }
546
547 write!(f, ">)")
548 }
549 TsType::Union(types) => {
550 write!(f, "Union(")?;
551
552 for (i, ty) in types.iter().enumerate() {
553 write!(f, "{:?}", ty)?;
554 if i < types.len() - 1 {
555 write!(f, " | ")?;
556 }
557 }
558
559 write!(f, ")")
560 }
561 TsType::Intersection(types) => {
562 write!(f, "Intersection(")?;
563
564 for (i, ty) in types.iter().enumerate() {
565 write!(f, "{:?}", ty)?;
566 if i < types.len() - 1 {
567 write!(f, " & ")?;
568 }
569 }
570
571 write!(f, ")")
572 }
573 TsType::Tuple(types) => {
574 write!(f, "Tuple([")?;
575
576 for (i, ty) in types.iter().enumerate() {
577 write!(f, "{:?}", ty)?;
578 if i < types.len() - 1 {
579 write!(f, ", ")?;
580 }
581 }
582
583 write!(f, "])")
584 }
585 }
586 }
587}
588
589impl TryFrom<&Type> for TsType {
592 type Error = TsTypeError;
593
594 #[track_caller]
595 fn try_from(ty: &Type) -> Result<Self, Self::Error> {
596 let rust_type_str = strip_type(ty)?;
597
598 if let Some(ts_type) = match_simple_type(&rust_type_str) {
600 return Ok(ts_type);
601 }
602
603 if let Some(captures) = OPTION_REGEX.captures(&rust_type_str) {
605 let inner_rust_type_str = &captures[1];
606 let ts_type = match_simple_type(inner_rust_type_str)
607 .unwrap_or(TsType::from_str(inner_rust_type_str)?);
608 return Ok(ts_type.or(ts_type!(undefined)));
609 }
610
611 if let Some(captures) = VEC_REGEX.captures(&rust_type_str) {
613 let inner_rust_type_str = &captures[1];
614 let ts_type = match_simple_type(inner_rust_type_str)
615 .unwrap_or(TsType::from_str(inner_rust_type_str)?);
616 return Ok(ts_type.in_array());
617 }
618
619 TsType::from_ts_str(&rust_type_str)
622 }
623}
624
625impl FromStr for TsType {
626 type Err = TsTypeError;
627
628 #[track_caller]
629 fn from_str(s: &str) -> Result<Self, Self::Err> {
630 TsType::from_ts_str(s)
631 }
632}
633
634pub trait ToTsType {
635 fn to_ts_type(&self) -> Result<TsType, TsTypeError>;
636}
637
638impl ToTsType for Type {
639 #[track_caller]
640 fn to_ts_type(&self) -> Result<TsType, TsTypeError> {
641 TsType::try_from(self)
642 }
643}
644
645impl ToTsType for &Type {
646 #[track_caller]
647 fn to_ts_type(&self) -> Result<TsType, TsTypeError> {
648 TsType::try_from(*self)
649 }
650}
651
652impl ToTsType for &str {
653 #[track_caller]
654 fn to_ts_type(&self) -> Result<TsType, TsTypeError> {
655 TsType::from_str(self)
656 }
657}
658
659impl ToTsType for String {
660 #[track_caller]
661 fn to_ts_type(&self) -> Result<TsType, TsTypeError> {
662 TsType::from_str(self.as_str())
663 }
664}
665
666#[track_caller]
670fn strip_type(ty: &Type) -> Result<String, TsTypeError> {
671 let location = Location::caller();
672 match ty {
673 Type::Group(group) => strip_type(&group.elem),
674 Type::Paren(paren) => strip_type(&paren.elem),
675 Type::Ptr(ptr) => strip_type(&ptr.elem),
676 Type::Reference(reference) => strip_type(&reference.elem),
677 Type::Slice(type_slice) => Ok(format!("[{}]", strip_type(&type_slice.elem)?)),
678 Type::Array(type_array) => Ok(format!("[{}; _]", strip_type(&type_array.elem)?)),
679 Type::Tuple(tuple) => {
680 if tuple.elems.is_empty() {
681 Ok("()".to_string())
682 } else {
683 let types = tuple
684 .elems
685 .iter()
686 .map(|elem| strip_type(elem))
687 .collect::<Result<Vec<_>, _>>()?
688 .join(", ");
689 Ok(format!("({})", types))
690 }
691 }
692 Type::Path(path) => {
693 let last_segment = path
694 .path
695 .segments
696 .last()
697 .ok_or_else(|| type_error_at!(location, "No segments found"))?;
698 let outer_type = last_segment.ident.to_string();
699
700 if last_segment.arguments.is_empty() {
701 Ok(outer_type)
702 } else {
703 let arguments = match &last_segment.arguments {
704 PathArguments::AngleBracketed(angle) => {
705 let args = angle
706 .args
707 .iter()
708 .map(|arg| match arg {
709 GenericArgument::Type(ty) => strip_type(ty),
710 _ => Err(type_error_at!(location, "Unsupported type argument.",)),
711 })
712 .collect::<Result<Vec<_>, _>>()?
713 .join(", ");
714 format!("<{}>", args)
715 }
716 PathArguments::Parenthesized(paren) => {
717 let inputs = paren
718 .inputs
719 .iter()
720 .map(strip_type)
721 .collect::<Result<Vec<_>, _>>()?
722 .join(", ");
723 format!("({})", inputs)
724 }
725 _ => String::new(),
726 };
727
728 Ok(format!("{}{}", last_segment.ident, arguments))
729 }
730 }
731
732 _ => Err(type_error_at!(location, "Unsupported type.")),
733 }
734}
735
736fn match_simple_type(rust_type: &str) -> Option<TsType> {
744 let simple_match = match rust_type {
745 "bool" => ts_type!(boolean),
747 "String" | "str" | "char" => ts_type!(string),
748 "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "f32" | "f64" => ts_type!(number),
750 "u64" | "i64" | "u128" | "i128" => ts_type!(bigint),
751
752 "U256" | "I256" => ts_type!(bigint),
754 "Address" => TsType::from_ts_str("`0x${string}`").unwrap(),
755
756 "BigInt" => ts_type!(bigint),
758 "Boolean" => ts_type!(boolean),
759 "JsString" => ts_type!(string),
760 "Number" => ts_type!(number),
761 "Object" => ts_type!(object),
762
763 "FixedPoint" => ts_type!(bigint),
765
766 _ => return None,
768 };
769 Some(simple_match)
770}
771
772#[cfg(test)]
773mod tests {
774 use super::*;
775
776 #[test]
777 fn test_contains() {
778 let base = ts_type!(string);
779 assert!(base.contains(&ts_type!(string)));
780 assert!(!base.contains(&ts_type!(number)));
781
782 let paren = ts_type!((string));
783 assert!(paren.contains(&ts_type!((string))));
784 assert!(paren.contains(&ts_type!(string)));
785 assert!(!paren.contains(&ts_type!(number)));
786
787 let array = ts_type!(string[]);
788 assert!(array.contains(&ts_type!(string[])));
789 assert!(array.contains(&ts_type!(string)));
790 assert!(!array.contains(&ts_type!(number)));
791
792 let tuple = ts_type!([string, number]);
793 assert!(tuple.contains(&ts_type!([string, number])));
794 assert!(tuple.contains(&ts_type!(string)));
795 assert!(tuple.contains(&ts_type!(number)));
796 assert!(!tuple.contains(&ts_type!(boolean)));
797
798 let union = ts_type!(string | number);
799 assert!(union.contains(&ts_type!(string | number)));
800 assert!(union.contains(&ts_type!(string)));
801 assert!(union.contains(&ts_type!(number)));
802 assert!(!union.contains(&ts_type!(boolean)));
803
804 let intersection = ts_type!(string & number);
805 assert!(intersection.contains(&ts_type!(string & number)));
806 assert!(intersection.contains(&ts_type!(string)));
807 assert!(intersection.contains(&ts_type!(number)));
808 assert!(!intersection.contains(&ts_type!(boolean)));
809
810 let generic = ts_type!(Set<string, number>);
811 assert!(generic.contains(&ts_type!(Set<string, number>)));
812 assert!(generic.contains(&ts_type!(Set)));
813 assert!(generic.contains(&ts_type!(string)));
814 assert!(generic.contains(&ts_type!(number)));
815 assert!(!generic.contains(&ts_type!(boolean)));
816
817 let indexed_access = ts_type!(Car[string]);
818 assert!(indexed_access.contains(&ts_type!(Car[string])));
819 assert!(indexed_access.contains(&ts_type!(Car)));
820 assert!(indexed_access.contains(&ts_type!(string)));
821 assert!(!indexed_access.contains(&ts_type!(number)));
822 }
823
824 #[test]
825 fn test_formatting() {
826 let base = ts_type!(string);
827 assert_eq!(base.to_string(), "string");
828
829 #[rustfmt::skip]
830 let paren = ts_type!(( string | number ));
831 assert_eq!(paren.to_string(), "(string | number)");
832
833 #[rustfmt::skip]
834 let array = ts_type!(string [ ]);
835 assert_eq!(array.to_string(), "string[]");
836
837 #[rustfmt::skip]
838 let generic = ts_type!(Set< string, number >);
839 assert_eq!(generic.to_string(), "Set<string, number>");
840
841 #[rustfmt::skip]
842 let _union = ts_type!(
843 | string
844 | number
845 | boolean
846 );
847 assert_eq!(_union.to_string(), "string | number | boolean");
848
849 #[rustfmt::skip]
850 let intersection = ts_type!( Foo & Bar & Baz);
851 assert_eq!(intersection.to_string(), "Foo & Bar & Baz");
852
853 #[rustfmt::skip]
854 let wrapped_intersection = ts_type!(Foo | Bar & Baz);
855 assert_eq!(wrapped_intersection.to_string(), "Foo | (Bar & Baz)");
856
857 let template_string = TsType::from_ts_str("`0x${string}`");
858 assert_eq!(template_string.unwrap().to_string(), "`0x${string}`");
859 }
860
861 #[test]
862 fn test_variable_parsing() {
863 let base = ts_type!(string);
864 let generic = ts_type!(Set<string>);
865 let group = ts_type!((string | number));
866 let intersection = ts_type!(string & number);
867 let _union = ts_type!(string | number);
868
869 let single = ts_type!((#base));
872 assert_eq!(single.to_string(), "string",);
873
874 let single_generic = ts_type!((#generic));
875 assert_eq!(single_generic.to_string(), "Set<string>");
876
877 let single_group = ts_type!((#group));
878 assert_eq!(single_group.to_string(), "(string | number)");
879
880 let single_intersection = ts_type!((#intersection));
881 assert_eq!(single_intersection.to_string(), "string & number");
882
883 let single_union = ts_type!((#_union));
884 assert_eq!(single_union.to_string(), "string | number");
885
886 let generic = ts_type!(Set<(#base)>);
889 assert_eq!(generic.to_string(), "Set<string>");
890
891 let generic_two = ts_type!(Set<(#base), (#_union)>);
892 assert_eq!(generic_two.to_string(), "Set<string, string | number>");
893
894 let start_union = ts_type!((#base) | true | false);
897 assert_eq!(start_union.to_string(), "string | true | false");
898
899 let mid_union = ts_type!(true | (#base) | false);
900 assert_eq!(mid_union.to_string(), "true | string | false");
901
902 let end_union = ts_type!(true | false | (#base));
903 assert_eq!(end_union.to_string(), "true | false | string");
904
905 let start_union_pair = ts_type!((#base) | true);
906 assert_eq!(start_union_pair.to_string(), "string | true");
907
908 let end_union_pair = ts_type!(true | (#base));
909 assert_eq!(end_union_pair.to_string(), "true | string");
910
911 let var_union = ts_type!((#base) | (#generic) | (#group));
912 assert_eq!(
913 var_union.to_string(),
914 "string | Set<string> | (string | number)"
915 );
916
917 let var_union_pair = ts_type!((#base) | (#generic));
918 assert_eq!(var_union_pair.to_string(), "string | Set<string>");
919
920 let start_intersection = ts_type!((#base) & true & false);
923 assert_eq!(start_intersection.to_string(), "string & true & false");
924
925 let mid_intersection = ts_type!(true & (#base) & false);
926 assert_eq!(mid_intersection.to_string(), "true & string & false");
927
928 let end_intersection = ts_type!(true & false & (#base));
929 assert_eq!(end_intersection.to_string(), "true & false & string");
930
931 let start_intersection_pair = ts_type!((#base) & true);
932 assert_eq!(start_intersection_pair.to_string(), "string & true");
933
934 let end_intersection_pair = ts_type!(true & (#base));
935 assert_eq!(end_intersection_pair.to_string(), "true & string");
936
937 let var_intersection = ts_type!((#base) & (#generic) & (#group));
938 assert_eq!(
939 var_intersection.to_string(),
940 "string & Set<string> & (string | number)"
941 );
942
943 let var_intersection_pair = ts_type!((#base) & (#generic));
944 assert_eq!(var_intersection_pair.to_string(), "string & Set<string>");
945 }
946}