Skip to content

Commit

Permalink
fix(napi-derive): fix union type generation for ts function notation (#…
Browse files Browse the repository at this point in the history
…1439)

* fix(backend): fix union type generation for ts function notation

* chore: update snapshot

* fix: naming
  • Loading branch information
h-a-n-a committed Jan 14, 2023
1 parent c61b202 commit 78b6e15
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 103 deletions.
72 changes: 36 additions & 36 deletions crates/backend/src/lib.rs
Expand Up @@ -56,40 +56,40 @@ napi_ast_impl! {
(Const, NapiConst),
}

pub(crate) static PRIMITIVE_TYPES: &[(&str, &str)] = &[
("JsUndefined", "undefined"),
("()", "undefined"),
("Undefined", "undefined"),
("JsNumber", "number"),
("i8", "number"),
("i16", "number"),
("i32", "number"),
("i64", "number"),
("f32", "number"),
("f64", "number"),
("u8", "number"),
("u16", "number"),
("u32", "number"),
("u64", "bigint"),
("i64n", "bigint"),
("u128", "bigint"),
("i128", "bigint"),
("usize", "bigint"),
("isize", "bigint"),
("JsBigInt", "bigint"),
("BigInt", "bigint"),
("JsBoolean", "boolean"),
("bool", "boolean"),
("JsString", "string"),
("String", "string"),
("str", "string"),
("Latin1String", "string"),
("Utf16String", "string"),
("char", "string"),
("Null", "null"),
("JsNull", "null"),
("null", "null"),
("Symbol", "symbol"),
("JsSymbol", "symbol"),
("JsFunction", "(...args: any[]) => any"),
pub(crate) static PRIMITIVE_TYPES: &[(&str, (&str, bool, bool))] = &[
("JsUndefined", ("undefined", false, false)),
("()", ("undefined", false, false)),
("Undefined", ("undefined", false, false)),
("JsNumber", ("number", false, false)),
("i8", ("number", false, false)),
("i16", ("number", false, false)),
("i32", ("number", false, false)),
("i64", ("number", false, false)),
("f32", ("number", false, false)),
("f64", ("number", false, false)),
("u8", ("number", false, false)),
("u16", ("number", false, false)),
("u32", ("number", false, false)),
("u64", ("bigint", false, false)),
("i64n", ("bigint", false, false)),
("u128", ("bigint", false, false)),
("i128", ("bigint", false, false)),
("usize", ("bigint", false, false)),
("isize", ("bigint", false, false)),
("JsBigInt", ("bigint", false, false)),
("BigInt", ("bigint", false, false)),
("JsBoolean", ("boolean", false, false)),
("bool", ("boolean", false, false)),
("JsString", ("string", false, false)),
("String", ("string", false, false)),
("str", ("string", false, false)),
("Latin1String", ("string", false, false)),
("Utf16String", ("string", false, false)),
("char", ("string", false, false)),
("Null", ("null", false, false)),
("JsNull", ("null", false, false)),
("null", ("null", false, false)),
("Symbol", ("symbol", false, false)),
("JsSymbol", ("symbol", false, false)),
("JsFunction", ("(...args: any[]) => any", true, false)),
];
165 changes: 98 additions & 67 deletions crates/backend/src/typegen.rs
Expand Up @@ -118,74 +118,75 @@ pub trait ToTypeDef {
fn to_type_def(&self) -> Option<TypeDef>;
}

static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
/// Mapping from `rust_type` to (`ts_type`, `is_ts_function_type_notation`, `is_ts_union_type`)
static KNOWN_TYPES: Lazy<HashMap<&'static str, (&'static str, bool, bool)>> = Lazy::new(|| {
let mut map = HashMap::default();
map.extend(crate::PRIMITIVE_TYPES.iter().cloned());
map.extend([
("JsObject", "object"),
("Object", "object"),
("Array", "unknown[]"),
("Value", "any"),
("Map", "Record<string, any>"),
("HashMap", "Record<{}, {}>"),
("ArrayBuffer", "ArrayBuffer"),
("Int8Array", "Int8Array"),
("Uint8Array", "Uint8Array"),
("Uint8ClampedArray", "Uint8ClampedArray"),
("Int16Array", "Int16Array"),
("Uint16Array", "Uint16Array"),
("Int32Array", "Int32Array"),
("Uint32Array", "Uint32Array"),
("Float32Array", "Float32Array"),
("Float64Array", "Float64Array"),
("BigInt64Array", "BigInt64Array"),
("BigUint64Array", "BigUint64Array"),
("DataView", "DataView"),
("DateTime", "Date"),
("Date", "Date"),
("JsDate", "Date"),
("JsBuffer", "Buffer"),
("Buffer", "Buffer"),
("Vec", "Array<{}>"),
("Result", "Error | {}"),
("Error", "Error"),
("JsError", "Error"),
("JsTypeError", "TypeError"),
("JsRangeError", "RangeError"),
("ClassInstance", "{}"),
("Either", "{} | {}"),
("Either3", "{} | {} | {}"),
("Either4", "{} | {} | {} | {}"),
("Either5", "{} | {} | {} | {} | {}"),
("Either6", "{} | {} | {} | {} | {} | {}"),
("Either7", "{} | {} | {} | {} | {} | {} | {}"),
("Either8", "{} | {} | {} | {} | {} | {} | {} | {}"),
("Either9", "{} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either10", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either11", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either12", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either13", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either14", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either15", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either16", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either17", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either18", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either19", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either20", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either21", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either22", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either23", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either24", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either25", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("Either26", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"),
("external", "object"),
("AbortSignal", "AbortSignal"),
("JsGlobal", "typeof global"),
("External", "ExternalObject<{}>"),
("unknown", "unknown"),
("Unknown", "unknown"),
("JsUnknown", "unknown"),
("This", "this")
("JsObject", ("object", false, false)),
("Object", ("object", false, false)),
("Array", ("unknown[]", false, false)),
("Value", ("any", false, false)),
("Map", ("Record<string, any>", false, false)),
("HashMap", ("Record<{}, {}>", false, false)),
("ArrayBuffer", ("ArrayBuffer", false, false)),
("Int8Array", ("Int8Array", false, false)),
("Uint8Array", ("Uint8Array", false, false)),
("Uint8ClampedArray", ("Uint8ClampedArray", false, false)),
("Int16Array", ("Int16Array", false, false)),
("Uint16Array", ("Uint16Array", false, false)),
("Int32Array", ("Int32Array", false, false)),
("Uint32Array", ("Uint32Array", false, false)),
("Float32Array", ("Float32Array", false, false)),
("Float64Array", ("Float64Array", false, false)),
("BigInt64Array", ("BigInt64Array", false, false)),
("BigUint64Array", ("BigUint64Array", false, false)),
("DataView", ("DataView", false, false)),
("DateTime", ("Date", false, false)),
("Date", ("Date", false, false)),
("JsDate", ("Date", false, false)),
("JsBuffer", ("Buffer", false, false)),
("Buffer", ("Buffer", false, false)),
("Vec", ("Array<{}>", false, false)),
("Result", ("Error | {}", false, true)),
("Error", ("Error", false, false)),
("JsError", ("Error", false, false)),
("JsTypeError", ("TypeError", false, false)),
("JsRangeError", ("RangeError", false, false)),
("ClassInstance", ("{}", false, false)),
("Either", ("{} | {}", false, true)),
("Either3", ("{} | {} | {}", false, true)),
("Either4", ("{} | {} | {} | {}", false, true)),
("Either5", ("{} | {} | {} | {} | {}", false, true)),
("Either6", ("{} | {} | {} | {} | {} | {}", false, true)),
("Either7", ("{} | {} | {} | {} | {} | {} | {}", false, true)),
("Either8", ("{} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either9", ("{} | {} | {} | {} | {} | {} | {} | {} | {}",false, true)),
("Either10", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either11", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either12", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either13", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either14", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either15", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either16", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either17", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either18", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either19", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either20", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either21", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either22", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either23", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either24", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either25", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("Either26", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
("external", ("object", false, false)),
("AbortSignal", ("AbortSignal", false, false)),
("JsGlobal", ("typeof global", false, false)),
("External", ("ExternalObject<{}>", false, false)),
("unknown", ("unknown", false, false)),
("Unknown", ("unknown", false, false)),
("JsUnknown", ("unknown", false, false)),
("This", ("this", false, false))
]);

map
Expand All @@ -209,6 +210,30 @@ fn fill_ty(template: &str, args: Vec<String>) -> String {
ret
}

fn is_ts_union_type(rust_ty: &str) -> bool {
KNOWN_TYPES
.get(rust_ty)
.map(|&(_, _, is_union_type)| is_union_type)
.unwrap_or(false)
}

fn is_ts_function_type_notation(ty: &Type) -> bool {
match ty {
Type::Path(syn::TypePath { qself: None, path }) => {
if let Some(syn::PathSegment { ident, .. }) = path.segments.last() {
let rust_ty = ident.to_string();
return KNOWN_TYPES
.get(&*rust_ty)
.map(|&(_, is_ts_fn, _)| is_ts_fn)
.unwrap_or(false);
}

false
}
_ => false,
}
}

pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (String, bool) {
match ty {
Type::Reference(r) => ty_to_ts_type(&r.elem, is_return_ty, is_struct_field),
Expand All @@ -235,13 +260,19 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S

if let Some(syn::PathSegment { ident, arguments }) = path.segments.last() {
let rust_ty = ident.to_string();
let is_ts_union_type = is_ts_union_type(&rust_ty);
let args = if let syn::PathArguments::AngleBracketed(arguments) = arguments {
arguments
.args
.iter()
.filter_map(|arg| match arg {
syn::GenericArgument::Type(generic_ty) => {
Some(ty_to_ts_type(generic_ty, false, false))
Some(ty_to_ts_type(generic_ty, false, false)).map(|(mut ty, is_struct_field)| {
if is_ts_union_type && is_ts_function_type_notation(generic_ty) {
ty = format!("({})", ty);
}
(ty, is_struct_field)
})
}
_ => None,
})
Expand Down Expand Up @@ -289,7 +320,7 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S
Some((rust_ty, false))
}
});
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
} else if let Some(&(known_ty, _, _)) = KNOWN_TYPES.get(rust_ty.as_str()) {
if known_ty.contains("{}") {
ts_ty = Some((
fill_ty(known_ty, args.into_iter().map(|(arg, _)| arg).collect()),
Expand Down
1 change: 1 addition & 0 deletions examples/napi/__test__/typegen.spec.ts.md
Expand Up @@ -79,6 +79,7 @@ Generated by [AVA](https://avajs.dev).
baz: number␊
}␊
export function eitherFromObjects(input: A | B | C): string␊
export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void␊
/** default enum values are continuos i32s start from 0 */␊
export const enum Kind {␊
/** Barks */␊
Expand Down
Binary file modified examples/napi/__test__/typegen.spec.ts.snap
Binary file not shown.
1 change: 1 addition & 0 deletions examples/napi/index.d.ts
Expand Up @@ -69,6 +69,7 @@ export interface C {
baz: number
}
export function eitherFromObjects(input: A | B | C): string
export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void
/** default enum values are continuos i32s start from 0 */
export const enum Kind {
/** Barks */
Expand Down
3 changes: 3 additions & 0 deletions examples/napi/src/either.rs
Expand Up @@ -127,3 +127,6 @@ pub fn either_from_objects(input: Either3<A, B, C>) -> String {
Either3::C(_) => "C".to_owned(),
}
}

#[napi]
pub fn either_bool_or_function(_input: Either<bool, JsFunction>) {}

0 comments on commit 78b6e15

Please sign in to comment.