Add skip_typescript attribute to prevent .d.ts emit (#2016)

* Add skip_typescript attribute to prevent .d.ts emit

* Add guide page for typescript attribute
This commit is contained in:
Joey Watts 2020-03-03 10:34:28 -05:00 committed by GitHub
parent 3f4acc453b
commit 7ffb5ed70c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 227 additions and 56 deletions

View File

@ -221,6 +221,7 @@ pub struct Function {
pub rust_attrs: Vec<syn::Attribute>, pub rust_attrs: Vec<syn::Attribute>,
pub rust_vis: syn::Visibility, pub rust_vis: syn::Visibility,
pub r#async: bool, pub r#async: bool,
pub generate_typescript: bool,
} }
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -231,6 +232,7 @@ pub struct Struct {
pub fields: Vec<StructField>, pub fields: Vec<StructField>,
pub comments: Vec<String>, pub comments: Vec<String>,
pub is_inspectable: bool, pub is_inspectable: bool,
pub generate_typescript: bool,
} }
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -243,6 +245,7 @@ pub struct StructField {
pub getter: Ident, pub getter: Ident,
pub setter: Ident, pub setter: Ident,
pub comments: Vec<String>, pub comments: Vec<String>,
pub generate_typescript: bool,
} }
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -252,6 +255,7 @@ pub struct Enum {
pub variants: Vec<Variant>, pub variants: Vec<Variant>,
pub comments: Vec<String>, pub comments: Vec<String>,
pub hole: u32, pub hole: u32,
pub generate_typescript: bool,
} }
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]

View File

@ -206,6 +206,7 @@ fn shared_function<'a>(func: &'a ast::Function, _intern: &'a Interner) -> Functi
Function { Function {
arg_names, arg_names,
name: &func.name, name: &func.name,
generate_typescript: func.generate_typescript,
} }
} }
@ -218,6 +219,7 @@ fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> {
.map(|v| shared_variant(v, intern)) .map(|v| shared_variant(v, intern))
.collect(), .collect(),
comments: e.comments.iter().map(|s| &**s).collect(), comments: e.comments.iter().map(|s| &**s).collect(),
generate_typescript: e.generate_typescript,
} }
} }
@ -307,6 +309,7 @@ fn shared_struct<'a>(s: &'a ast::Struct, intern: &'a Interner) -> Struct<'a> {
.collect(), .collect(),
comments: s.comments.iter().map(|s| &**s).collect(), comments: s.comments.iter().map(|s| &**s).collect(),
is_inspectable: s.is_inspectable, is_inspectable: s.is_inspectable,
generate_typescript: s.generate_typescript,
} }
} }
@ -318,6 +321,7 @@ fn shared_struct_field<'a>(s: &'a ast::StructField, intern: &'a Interner) -> Str
}, },
readonly: s.readonly, readonly: s.readonly,
comments: s.comments.iter().map(|s| &**s).collect(), comments: s.comments.iter().map(|s| &**s).collect(),
generate_typescript: s.generate_typescript,
} }
} }

View File

@ -111,7 +111,7 @@ impl<'a> Context<'a> {
&mut self, &mut self,
export_name: &str, export_name: &str,
contents: &str, contents: &str,
comments: Option<String>, comments: Option<&str>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let definition_name = generate_identifier(export_name, &mut self.defined_identifiers); let definition_name = generate_identifier(export_name, &mut self.defined_identifiers);
if contents.starts_with("class") && definition_name != export_name { if contents.starts_with("class") && definition_name != export_name {
@ -119,9 +119,8 @@ impl<'a> Context<'a> {
} }
let contents = contents.trim(); let contents = contents.trim();
if let Some(ref c) = comments { if let Some(c) = comments {
self.globals.push_str(c); self.globals.push_str(c);
self.typescript.push_str(c);
} }
let global = match self.config.mode { let global = match self.config.mode {
OutputMode::Node { OutputMode::Node {
@ -804,7 +803,7 @@ impl<'a> Context<'a> {
dst.push_str("}\n"); dst.push_str("}\n");
ts_dst.push_str("}\n"); ts_dst.push_str("}\n");
self.export(&name, &dst, Some(class.comments.clone()))?; self.export(&name, &dst, Some(&class.comments))?;
self.typescript.push_str(&ts_dst); self.typescript.push_str(&ts_dst);
Ok(()) Ok(())
@ -2153,41 +2152,58 @@ impl<'a> Context<'a> {
// on what's being exported. // on what's being exported.
match kind { match kind {
Kind::Export(export) => { Kind::Export(export) => {
let ts_sig = match export.generate_typescript {
true => Some(ts_sig.as_str()),
false => None,
};
let docs = format_doc_comments(&export.comments, Some(js_doc)); let docs = format_doc_comments(&export.comments, Some(js_doc));
match &export.kind { match &export.kind {
AuxExportKind::Function(name) => { AuxExportKind::Function(name) => {
self.export(&name, &format!("function{}", code), Some(docs))?; if let Some(ts_sig) = ts_sig {
self.globals.push_str("\n"); self.typescript.push_str(&docs);
self.typescript.push_str("export function "); self.typescript.push_str("export function ");
self.typescript.push_str(&name); self.typescript.push_str(&name);
self.typescript.push_str(&ts_sig); self.typescript.push_str(ts_sig);
self.typescript.push_str(";\n"); self.typescript.push_str(";\n");
} }
self.export(&name, &format!("function{}", code), Some(&docs))?;
self.globals.push_str("\n");
}
AuxExportKind::Constructor(class) => { AuxExportKind::Constructor(class) => {
let exported = require_class(&mut self.exported_classes, class); let exported = require_class(&mut self.exported_classes, class);
if exported.has_constructor { if exported.has_constructor {
bail!("found duplicate constructor for class `{}`", class); bail!("found duplicate constructor for class `{}`", class);
} }
exported.has_constructor = true; exported.has_constructor = true;
exported.push(&docs, "constructor", "", &code, &ts_sig); exported.push(&docs, "constructor", "", &code, ts_sig);
} }
AuxExportKind::Getter { class, field } => { AuxExportKind::Getter { class, field } => {
let ret_ty = ts_ret_ty.unwrap(); let ret_ty = match export.generate_typescript {
true => match &ts_ret_ty {
Some(s) => Some(s.as_str()),
_ => None,
},
false => None,
};
let exported = require_class(&mut self.exported_classes, class); let exported = require_class(&mut self.exported_classes, class);
exported.push_getter(&docs, field, &code, &ret_ty); exported.push_getter(&docs, field, &code, ret_ty);
} }
AuxExportKind::Setter { class, field } => { AuxExportKind::Setter { class, field } => {
let arg_ty = ts_arg_tys[0].clone(); let arg_ty = match export.generate_typescript {
true => Some(ts_arg_tys[0].as_str()),
false => None,
};
let exported = require_class(&mut self.exported_classes, class); let exported = require_class(&mut self.exported_classes, class);
exported.push_setter(&docs, field, &code, &arg_ty, might_be_optional_field); exported.push_setter(&docs, field, &code, arg_ty, might_be_optional_field);
} }
AuxExportKind::StaticFunction { class, name } => { AuxExportKind::StaticFunction { class, name } => {
let exported = require_class(&mut self.exported_classes, class); let exported = require_class(&mut self.exported_classes, class);
exported.push(&docs, name, "static ", &code, &ts_sig); exported.push(&docs, name, "static ", &code, ts_sig);
} }
AuxExportKind::Method { class, name, .. } => { AuxExportKind::Method { class, name, .. } => {
let exported = require_class(&mut self.exported_classes, class); let exported = require_class(&mut self.exported_classes, class);
exported.push(&docs, name, "", &code, &ts_sig); exported.push(&docs, name, "", &code, ts_sig);
} }
} }
} }
@ -2865,19 +2881,27 @@ impl<'a> Context<'a> {
} }
fn generate_enum(&mut self, enum_: &AuxEnum) -> Result<(), Error> { fn generate_enum(&mut self, enum_: &AuxEnum) -> Result<(), Error> {
let docs = format_doc_comments(&enum_.comments, None);
let mut variants = String::new(); let mut variants = String::new();
if enum_.generate_typescript {
self.typescript.push_str(&docs);
self.typescript self.typescript
.push_str(&format!("export enum {} {{", enum_.name)); .push_str(&format!("export enum {} {{", enum_.name));
}
for (name, value) in enum_.variants.iter() { for (name, value) in enum_.variants.iter() {
variants.push_str(&format!("{}:{},", name, value)); variants.push_str(&format!("{}:{},", name, value));
if enum_.generate_typescript {
self.typescript.push_str(&format!("\n {},", name)); self.typescript.push_str(&format!("\n {},", name));
} }
}
if enum_.generate_typescript {
self.typescript.push_str("\n}\n"); self.typescript.push_str("\n}\n");
}
self.export( self.export(
&enum_.name, &enum_.name,
&format!("Object.freeze({{ {} }})", variants), &format!("Object.freeze({{ {} }})", variants),
Some(format_doc_comments(&enum_.comments, None)), Some(&docs),
)?; )?;
Ok(()) Ok(())
@ -3163,12 +3187,20 @@ fn require_class<'a>(
} }
impl ExportedClass { impl ExportedClass {
fn push(&mut self, docs: &str, function_name: &str, function_prefix: &str, js: &str, ts: &str) { fn push(
&mut self,
docs: &str,
function_name: &str,
function_prefix: &str,
js: &str,
ts: Option<&str>,
) {
self.contents.push_str(docs); self.contents.push_str(docs);
self.contents.push_str(function_prefix); self.contents.push_str(function_prefix);
self.contents.push_str(function_name); self.contents.push_str(function_name);
self.contents.push_str(js); self.contents.push_str(js);
self.contents.push_str("\n"); self.contents.push_str("\n");
if let Some(ts) = ts {
self.typescript.push_str(docs); self.typescript.push_str(docs);
self.typescript.push_str(" "); self.typescript.push_str(" ");
self.typescript.push_str(function_prefix); self.typescript.push_str(function_prefix);
@ -3176,11 +3208,15 @@ impl ExportedClass {
self.typescript.push_str(ts); self.typescript.push_str(ts);
self.typescript.push_str(";\n"); self.typescript.push_str(";\n");
} }
}
/// Used for adding a getter to a class, mainly to ensure that TypeScript /// Used for adding a getter to a class, mainly to ensure that TypeScript
/// generation is handled specially. /// generation is handled specially.
fn push_getter(&mut self, docs: &str, field: &str, js: &str, ret_ty: &str) { fn push_getter(&mut self, docs: &str, field: &str, js: &str, ret_ty: Option<&str>) {
self.push_accessor(docs, field, js, "get ", ret_ty); self.push_accessor(docs, field, js, "get ");
if let Some(ret_ty) = ret_ty {
self.push_accessor_ts(field, ret_ty);
}
self.readable_properties.push(field.to_string()); self.readable_properties.push(field.to_string());
} }
@ -3191,28 +3227,18 @@ impl ExportedClass {
docs: &str, docs: &str,
field: &str, field: &str,
js: &str, js: &str,
ret_ty: &str, ret_ty: Option<&str>,
might_be_optional_field: bool, might_be_optional_field: bool,
) { ) {
let (has_setter, is_optional) = self.push_accessor(docs, field, js, "set ", ret_ty); self.push_accessor(docs, field, js, "set ");
if let Some(ret_ty) = ret_ty {
let (has_setter, is_optional) = self.push_accessor_ts(field, ret_ty);
*has_setter = true; *has_setter = true;
*is_optional = might_be_optional_field; *is_optional = might_be_optional_field;
} }
}
fn push_accessor( fn push_accessor_ts(&mut self, field: &str, ret_ty: &str) -> (&mut bool, &mut bool) {
&mut self,
docs: &str,
field: &str,
js: &str,
prefix: &str,
ret_ty: &str,
) -> (&mut bool, &mut bool) {
self.contents.push_str(docs);
self.contents.push_str(prefix);
self.contents.push_str(field);
self.contents.push_str(js);
self.contents.push_str("\n");
let (ty, has_setter, is_optional) = self let (ty, has_setter, is_optional) = self
.typescript_fields .typescript_fields
.entry(field.to_string()) .entry(field.to_string())
@ -3221,6 +3247,14 @@ impl ExportedClass {
*ty = ret_ty.to_string(); *ty = ret_ty.to_string();
(has_setter, is_optional) (has_setter, is_optional)
} }
fn push_accessor(&mut self, docs: &str, field: &str, js: &str, prefix: &str) {
self.contents.push_str(docs);
self.contents.push_str(prefix);
self.contents.push_str(field);
self.contents.push_str(js);
self.contents.push_str("\n");
}
} }
#[test] #[test]

View File

@ -454,6 +454,7 @@ impl<'a> Context<'a> {
comments: concatenate_comments(&export.comments), comments: concatenate_comments(&export.comments),
arg_names: Some(export.function.arg_names), arg_names: Some(export.function.arg_names),
kind, kind,
generate_typescript: export.function.generate_typescript,
}, },
); );
Ok(()) Ok(())
@ -767,6 +768,7 @@ impl<'a> Context<'a> {
.iter() .iter()
.map(|v| (v.name.to_string(), v.value)) .map(|v| (v.name.to_string(), v.value))
.collect(), .collect(),
generate_typescript: enum_.generate_typescript,
}; };
self.aux.enums.push(aux); self.aux.enums.push(aux);
Ok(()) Ok(())
@ -799,6 +801,7 @@ impl<'a> Context<'a> {
class: struct_.name.to_string(), class: struct_.name.to_string(),
field: field.name.to_string(), field: field.name.to_string(),
}, },
generate_typescript: field.generate_typescript,
}, },
); );
@ -824,6 +827,7 @@ impl<'a> Context<'a> {
class: struct_.name.to_string(), class: struct_.name.to_string(),
field: field.name.to_string(), field: field.name.to_string(),
}, },
generate_typescript: field.generate_typescript,
}, },
); );
} }
@ -831,6 +835,7 @@ impl<'a> Context<'a> {
name: struct_.name.to_string(), name: struct_.name.to_string(),
comments: concatenate_comments(&struct_.comments), comments: concatenate_comments(&struct_.comments),
is_inspectable: struct_.is_inspectable, is_inspectable: struct_.is_inspectable,
generate_typescript: struct_.generate_typescript,
}; };
self.aux.structs.push(aux); self.aux.structs.push(aux);
@ -1048,6 +1053,7 @@ impl<'a> Context<'a> {
comments: String::new(), comments: String::new(),
arg_names: None, arg_names: None,
kind, kind,
generate_typescript: true,
}; };
assert!(self.aux.export_map.insert(id, export).is_none()); assert!(self.aux.export_map.insert(id, export).is_none());
} }

View File

@ -73,6 +73,8 @@ pub struct AuxExport {
pub arg_names: Option<Vec<String>>, pub arg_names: Option<Vec<String>>,
/// What kind of function this is and where it shows up /// What kind of function this is and where it shows up
pub kind: AuxExportKind, pub kind: AuxExportKind,
/// Whether typescript bindings should be generated for this export.
pub generate_typescript: bool,
} }
/// All possible kinds of exports from a wasm module. /// All possible kinds of exports from a wasm module.
@ -131,7 +133,10 @@ pub struct AuxEnum {
/// The copied Rust comments to forward to JS /// The copied Rust comments to forward to JS
pub comments: String, pub comments: String,
/// A list of variants with their name and value /// A list of variants with their name and value
/// and whether typescript bindings should be generated for each variant
pub variants: Vec<(String, u32)>, pub variants: Vec<(String, u32)>,
/// Whether typescript bindings should be generated for this enum.
pub generate_typescript: bool,
} }
#[derive(Debug)] #[derive(Debug)]
@ -142,6 +147,8 @@ pub struct AuxStruct {
pub comments: String, pub comments: String,
/// Whether to generate helper methods for inspecting the class /// Whether to generate helper methods for inspecting the class
pub is_inspectable: bool, pub is_inspectable: bool,
/// Whether typescript bindings should be generated for this struct.
pub generate_typescript: bool,
} }
/// All possible types of imports that can be imported by a wasm module. /// All possible types of imports that can be imported by a wasm module.

View File

@ -51,6 +51,7 @@ macro_rules! attrgen {
(vendor_prefix, VendorPrefix(Span, Ident)), (vendor_prefix, VendorPrefix(Span, Ident)),
(variadic, Variadic(Span)), (variadic, Variadic(Span)),
(typescript_custom_section, TypescriptCustomSection(Span)), (typescript_custom_section, TypescriptCustomSection(Span)),
(skip_typescript, SkipTypescript(Span)),
(start, Start(Span)), (start, Start(Span)),
(skip, Skip(Span)), (skip, Skip(Span)),
(typescript_type, TypeScriptType(Span, String, Span)), (typescript_type, TypeScriptType(Span, String, Span)),
@ -354,9 +355,11 @@ impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
getter: Ident::new(&getter, Span::call_site()), getter: Ident::new(&getter, Span::call_site()),
setter: Ident::new(&setter, Span::call_site()), setter: Ident::new(&setter, Span::call_site()),
comments, comments,
generate_typescript: attrs.skip_typescript().is_none(),
}); });
attrs.check_used()?; attrs.check_used()?;
} }
let generate_typescript = attrs.skip_typescript().is_none();
let comments: Vec<String> = extract_doc_comments(&self.attrs); let comments: Vec<String> = extract_doc_comments(&self.attrs);
attrs.check_used()?; attrs.check_used()?;
Ok(ast::Struct { Ok(ast::Struct {
@ -365,6 +368,7 @@ impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
fields, fields,
comments, comments,
is_inspectable, is_inspectable,
generate_typescript,
}) })
} }
} }
@ -720,6 +724,7 @@ fn function_from_decl(
rust_attrs: attrs, rust_attrs: attrs,
rust_vis: vis, rust_vis: vis,
r#async: sig.asyncness.is_some(), r#async: sig.asyncness.is_some(),
generate_typescript: opts.skip_typescript().is_none(),
}, },
method_self, method_self,
)) ))
@ -798,11 +803,12 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
}; };
f.macro_parse(program, opts)?; f.macro_parse(program, opts)?;
} }
syn::Item::Enum(e) => { syn::Item::Enum(mut e) => {
if let Some(opts) = opts { let opts = match opts {
opts.check_used()?; Some(opts) => opts,
} None => BindgenAttrs::find(&mut e.attrs)?,
e.macro_parse(program, (tokens,))?; };
e.macro_parse(program, (tokens, opts))?;
} }
syn::Item::Const(mut c) => { syn::Item::Const(mut c) => {
let opts = match opts { let opts = match opts {
@ -1032,15 +1038,17 @@ fn import_enum(enum_: syn::ItemEnum, program: &mut ast::Program) -> Result<(), D
Ok(()) Ok(())
} }
impl<'a> MacroParse<(&'a mut TokenStream,)> for syn::ItemEnum { impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
fn macro_parse( fn macro_parse(
self, self,
program: &mut ast::Program, program: &mut ast::Program,
(tokens,): (&'a mut TokenStream,), (tokens, opts): (&'a mut TokenStream, BindgenAttrs),
) -> Result<(), Diagnostic> { ) -> Result<(), Diagnostic> {
if self.variants.len() == 0 { if self.variants.len() == 0 {
bail_span!(self, "cannot export empty enums to JS"); bail_span!(self, "cannot export empty enums to JS");
} }
let generate_typescript = opts.skip_typescript().is_none();
opts.check_used()?;
// Check if the first value is a string literal // Check if the first value is a string literal
match self.variants[0].discriminant { match self.variants[0].discriminant {
@ -1141,8 +1149,8 @@ impl<'a> MacroParse<(&'a mut TokenStream,)> for syn::ItemEnum {
variants, variants,
comments, comments,
hole, hole,
generate_typescript,
}); });
Ok(()) Ok(())
} }
} }

View File

@ -100,6 +100,7 @@ macro_rules! shared_api {
name: &'a str, name: &'a str,
variants: Vec<EnumVariant<'a>>, variants: Vec<EnumVariant<'a>>,
comments: Vec<&'a str>, comments: Vec<&'a str>,
generate_typescript: bool,
} }
struct EnumVariant<'a> { struct EnumVariant<'a> {
@ -110,6 +111,7 @@ macro_rules! shared_api {
struct Function<'a> { struct Function<'a> {
arg_names: Vec<String>, arg_names: Vec<String>,
name: &'a str, name: &'a str,
generate_typescript: bool,
} }
struct Struct<'a> { struct Struct<'a> {
@ -117,12 +119,14 @@ macro_rules! shared_api {
fields: Vec<StructField<'a>>, fields: Vec<StructField<'a>>,
comments: Vec<&'a str>, comments: Vec<&'a str>,
is_inspectable: bool, is_inspectable: bool,
generate_typescript: bool,
} }
struct StructField<'a> { struct StructField<'a> {
name: &'a str, name: &'a str,
readonly: bool, readonly: bool,
comments: Vec<&'a str>, comments: Vec<&'a str>,
generate_typescript: bool,
} }
struct LocalModule<'a> { struct LocalModule<'a> {

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
wasm-bindgen = { path = '../..' } wasm-bindgen = { path = '../..' }
web-sys = { path = '../web-sys', features = [ 'HtmlElement', 'Node', 'Document' ] } web-sys = { path = '../web-sys', features = [ 'HtmlElement', 'Node', 'Document' ] }
js-sys = { path = '../js-sys' }
[lib] [lib]
crate-type = ['cdylib'] crate-type = ['cdylib']

View File

@ -1,5 +1,6 @@
pub mod custom_section; pub mod custom_section;
pub mod getters_setters; pub mod getters_setters;
pub mod omit_definition;
pub mod opt_args_and_ret; pub mod opt_args_and_ret;
pub mod optional_fields; pub mod optional_fields;
pub mod simple_fn; pub mod simple_fn;

View File

@ -0,0 +1,34 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen(typescript_custom_section)]
const TYPE_GET_VALUE: &'static str =
"export function take_function(func: (x: number) => void): void;";
#[wasm_bindgen(skip_typescript)]
pub fn take_function(_: js_sys::Function) {}
#[wasm_bindgen]
pub struct MyExportedStruct {
#[wasm_bindgen(skip_typescript)]
pub field: bool,
}
#[wasm_bindgen]
impl MyExportedStruct {
#[wasm_bindgen(skip_typescript)]
pub fn method(&mut self) {
self.field = !self.field;
}
#[wasm_bindgen(skip_typescript)]
pub fn static_func() {
panic!("oh no!");
}
}
#[wasm_bindgen(skip_typescript)]
pub enum MyEnum {
One,
Two,
Three,
}

View File

@ -0,0 +1,26 @@
import * as wbg from '../pkg/typescript_tests';
wbg.take_function((value) => {
// `value` should be inferred as `number` because of the
// custom typescript section. If `typescript = false` does
// not prevent the generation of a signature that takes any
// function, then this will trigger a noImplicitAny error.
console.log(value);
});
declare function assert<T>(message: T): void;
type EnableIfEnum = "MyEnum" extends keyof typeof wbg ? never : string;
assert<EnableIfEnum>("`MyEnum` type should not be exported.");
type EnableIfStruct = "MyStruct" extends keyof typeof wbg ? never : string;
assert<EnableIfStruct>("`MyStruct` type should not be exported.");
type EnableIfField = "field" extends keyof wbg.MyExportedStruct ? never : string;
assert<EnableIfField>("`field` should not exist on `MyExportedStruct`.");
type EnableIfMethod = "method" extends keyof wbg.MyExportedStruct ? never : string;
assert<EnableIfMethod>("`method` should not exist on `MyExportedStruct`.");
type EnableIfStaticMethod = "static_func" extends keyof typeof wbg.MyExportedStruct ? never : string;
assert<EnableIfStaticMethod>("`static_func` should not exist on `MyExportedStruct`.");

View File

@ -0,0 +1,42 @@
# `skip_typescript`
By default, Rust exports exposed to JavaScript will generate TypeScript definitions (unless `--no-typescript` is used). The `skip_typescript` attribute can be used to disable type generation per function, enum, struct, or field. For example:
```rust
#[wasm_bindgen(skip_typescript)]
pub enum MyHiddenEnum {
One,
Two,
Three
}
#[wasm_bindgen]
pub struct MyPoint {
pub x: u32,
#[wasm_bindgen(skip_typescript)]
pub y: u32,
}
#[wasm_bindgen]
impl MyPoint {
#[wasm_bindgen(skip_typescript)]
pub fn stringify(&self) -> String {
format!("({}, {})", self.x, self.y)
}
}
```
Will generate the following `.d.ts` file:
```ts
/* tslint:disable */
/* eslint-disable */
export class MyPoint {
free(): void;
x: number;
}
```
When combined with [the `typescript_custom_section` attribute](typescript_custom_section.html), this can be used to manually specify more specific function types instead of using the generated definitions.